JPA:ネイティブクエリ結果セットをPOJOクラスコレクションに変換する方法


174

プロジェクトでJPAを使用しています。

5つのテーブルで結合操作を実行する必要があるクエリに到達しました。そこで、5つのフィールドを返すネイティブクエリを作成しました。

次に、結果オブジェクトを、同じ5つの文字列を含むjava POJOクラスに変換します。

JPAでその結果を直接POJOオブジェクトリストにキャストする方法はありますか?

私は次の解決策を見つけました...

@NamedNativeQueries({  
    @NamedNativeQuery(  
        name = "nativeSQL",  
        query = "SELECT * FROM Actors",  
        resultClass = db.Actor.class),  
    @NamedNativeQuery(  
        name = "nativeSQL2",  
        query = "SELECT COUNT(*) FROM Actors",  
        resultClass = XXXXX) // <--------------- problem  
})  

ここで、resultClassで、実際のJPAエンティティであるクラスを提供する必要がありますか?または、同じ列名を含む任意のJAVA POJOクラスに変換できますか?


この答えを確認してください。それは完全な答えを持っています:stackoverflow.com/a/50365522/3073945
Ms. Sajedul Karim '16

彼は春ではなくjpaを使用しています
彼は

回答:


103

JPAは、SqlResultSetMappingネイティブクエリからの戻り値をエンティティにマップできるようにしますまたはカスタムクラス

EDIT JPA 1.0では、非エンティティークラスへのマッピングは許可されていません。JPA 2.1でのみ、戻り値をJavaクラスにマップするためにConstructorResultが追加されました。

また、カウントの取得に関するOPの問題については、単一の結果セットマッピングを定義することで十分です。 ColumnResult


1
返信いただきありがとうございます。ここでは、 "@ EntityResult"および "@FieldResult"アノテーションが付いたJavaエンティティークラスを持つエンティティーに結果をマッピングしています。それはいいです。しかし、ここで私はより明確にする必要があります。結果をマッピングしているクラスはJPAエンティティークラスである必要がありますか?または、結果セットの列として必要なすべての変数を持つエンティティーの購入ではない単純なPOJOクラスを使用できますか。
Gunjan Shah 2012

1
@GunjanShah:知る最良の方法は、それを試してみることです:)また、エンティティは、いくつかの注釈を付けただけで、同じpojoです。あなたがそれを永続化しようとしない限り、それはpojoのままです。
Denis Tulskiy

2
これを試したところ、クラスが既知のエンティティではないというエラーが発生しました。私は、ネイティブクエリを使用する代わりに、このアプローチstackoverflow.com/questions/5024533/…を使用することになりました。
FGreg 2013年

2
@EdwinDalorzo:それはjpa 1.0にも当てはまります。jpa 2.1 ではConstructorResult、パラメーターの1つとして追加されSqlResultSetMapping、コンストラクターですべてのフィールドが設定されたpojo を使用できるようになりました。答えを更新します。
Denis Tulskiy 2014年

4
別の苦い真実がわかります。ConstructorResultはPOJOにマップできます。主キーに-それでもエンティティに@Idが必要です...ばかげて正しいですか?
Arnab Dutta

210

私はこれに対するいくつかの解決策を見つけました。

マップされたエンティティの使用(JPA 2.0)

JPA 2.0を使用すると、ネイティブクエリをPOJOにマップすることはできません。エンティティでのみ実行できます。

例えば:

Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();

ただし、この場合、Jediはマップされたエンティティクラスである必要があります。

ここでチェックされていない警告を回避する別の方法は、名前付きのネイティブクエリを使用することです。したがって、エンティティでネイティブクエリを宣言すると、

@NamedNativeQuery(
 name="jedisQry", 
 query = "SELECT name,age FROM jedis_table", 
 resultClass = Jedi.class)

その後、簡単に行うことができます。

TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();

これはより安全ですが、マップされたエンティティの使用に制限されています。

手動マッピング

(JPA 2.1が登場する前に)少し実験した解決策は、少しのリフレクションを使用してPOJOコンストラクターに対してマッピングを行うことでした。

public static <T> T map(Class<T> type, Object[] tuple){
   List<Class<?>> tupleTypes = new ArrayList<>();
   for(Object field : tuple){
      tupleTypes.add(field.getClass());
   }
   try {
      Constructor<T> ctor = type.getConstructor(tupleTypes.toArray(new Class<?>[tuple.length]));
      return ctor.newInstance(tuple);
   } catch (Exception e) {
      throw new RuntimeException(e);
   }
}

このメソッドは、基本的にはタプル配列(ネイティブクエリによって返される)を取得し、同じ数のフィールドと同じ型のコンストラクターを探すことにより、提供されたPOJOクラスにマップします。

次に、次のような便利な方法を使用できます。

public static <T> List<T> map(Class<T> type, List<Object[]> records){
   List<T> result = new LinkedList<>();
   for(Object[] record : records){
      result.add(map(type, record));
   }
   return result;
}

public static <T> List<T> getResultList(Query query, Class<T> type){
  @SuppressWarnings("unchecked")
  List<Object[]> records = query.getResultList();
  return map(type, records);
}

そして、このテクニックは次のように簡単に使用できます。

Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);

@SqlResultSetMappingを使用したJPA 2.1

JPA 2.1の登場により、@ SqlResultSetMappingアノテーションを使用して問題を解決できるようになりました。

エンティティのどこかにマッピングする結果セットを宣言する必要があります。

@SqlResultSetMapping(name="JediResult", classes = {
    @ConstructorResult(targetClass = Jedi.class, 
    columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})

そして、私たちは単に行います:

Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();

もちろん、この場合Jedi、マップされたエンティティである必要はありません。通常のPOJOにすることができます。

XMLマッピングの使用

私は@SqlResultSetMappingエンティティにこれらのすべてをかなり侵襲的に追加することを見つけた人の1人であり、エンティティ内の名前付きクエリの定義が特に嫌いなので、代わりにMETA-INF/orm.xmlファイルでこれをすべて行います。

<named-native-query name="GetAllJedi" result-set-mapping="JediMapping">
    <query>SELECT name,age FROM jedi_table</query>
</named-native-query>

<sql-result-set-mapping name="JediMapping">
        <constructor-result target-class="org.answer.model.Jedi">
            <column name="name" class="java.lang.String"/>
            <column name="age" class="java.lang.Integer"/>
        </constructor-result>
    </sql-result-set-mapping>

そして、それらは私が知っているすべてのソリューションです。最後の2つは、JPA 2.1を使用できる場合の理想的な方法です。


1
補足:JPA2.1に依存するJPA 2.0アプローチを使用したところ、失敗しました。したがって、おそらくこれには下位互換性はありません...
メンバーサウンド'19

1
「エンティティ内のどこか」とはどういう意味ですか?私のPojoはJPAエンティティではありません私のPOJOで@SqlResultSetMappingを宣言できませんか?JPA 2.1ソリューションに興味があります。もう少し正確にしてください。
Alboz、2015

3
@Alboz @SqlResultSetMappingは、JPAがメタデータを読み取るためにエンティティに配置する必要があります。JPAがPOJOを検査することは期待できません。マッピングを配置するエンティティは無関係であり、POJOの結果により関連しているエンティティである可能性があります。あるいは、完全に無関係なエンティティとの結合を回避するために、マッピングをXMLで表現することもできます。
Edwin Dalorzo、2015

1
コンストラクタの結果で、ネストされたクラスを持つクラスを使用することはできますか?
chrismarx 2015年

5
JPA 2.1を使用する場合@SqlResultSetMappingJediクラスがすべて引数のコンストラクターを@ColumnResult必要とし、注釈がtype変換に追加された属性を必要とする場合があるため、暗黙的ではない場合があります(type = ZonedDateTime.class一部の列に追加する必要がありました )。
Glenn

11

はい、JPA 2.1では簡単です。非常に便利なアノテーションがあります。彼らはあなたの人生を簡素化します。

最初にネイティブクエリを宣言し、次に結果セットマッピング(データベースからPOJOに返されるデータのマッピングを定義する)を宣言します。参照するPOJOクラスを記述します(簡潔にするため、ここには含まれていません)。最後に重要なことですが、DAO(たとえば)でメソッドを作成してクエリを呼び出します。これは、dropwizard(1.0.0)アプリで私に役立ちました。

最初にエンティティクラスでネイティブクエリを宣言します。

@NamedNativeQuery (
name = "domain.io.MyClass.myQuery",
query = "Select a.colA, a.colB from Table a",
resultSetMapping = "mappinMyNativeQuery")   // must be the same name as in the SqlResultSetMapping declaration

その下に結果セットマッピング宣言を追加できます。

@SqlResultSetMapping(
name = "mapppinNativeQuery",  // same as resultSetMapping above in NativeQuery
   classes = {
      @ConstructorResult( 
          targetClass = domain.io.MyMapping.class,
          columns = {
               @ColumnResult( name = "colA", type = Long.class),  
               @ColumnResult( name = "colB", type = String.class)
          }
      )
   } 
)

DAOの後半では、クエリを次のように参照できます。

public List<domain.io.MyMapping> findAll() {
        return (namedQuery("domain.io.MyClass.myQuery").list());
    }

それでおしまい。


いい答えですが、最初の@ColumnResultアノテーションの後の括弧が抜けていると思います。
mwatzer

コードには誤りがありますが、修正は簡単です。例: "resulSetMapping ="は "resultSetMapping ="である必要があります
Zbyszek

3
別の苦い真実がわかります。NamedNativeQueryとSqlResultSetMappingは@Entityクラスにある必要があります
Arnab Dutta

10

を使用する場合Spring-jpa、これは回答とこの質問の補足です。欠陥があれば修正してください。私は主に3つの方法を使用Object[]して、実際にどのようなニーズを満たすかに基づいて、「pojo へのマッピング結果」を達成しました。

  1. JPA組み込みメソッドで十分です。
  2. JPA組み込みメソッドは十分ではありませんが、それを使用してカスタマイズさsqlれたEntityもので十分です。
  3. 前者2は失敗し、私はを使用する必要がありますnativeQuery。次に例を示します。予想されるpojo:

    public class Antistealingdto {
    
        private String secretKey;
    
        private Integer successRate;
    
        // GETTERs AND SETTERs
    
        public Antistealingdto(String secretKey, Integer successRate) {
            this.secretKey = secretKey;
            this.successRate = successRate;
        }
    }

方法1:pojoをインターフェースに変更します。

public interface Antistealingdto {
    String getSecretKey();
    Integer getSuccessRate();
}

そしてリポジトリ:

interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
    Antistealingdto findById(Long id);
}

方法2:リポジトリ:

@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);

注:POJOコンストラクターのパラメーターシーケンスは、POJO定義とSQLの両方で同一である必要があります。

方法3:Edwin Dalorzoの回答の例として@SqlResultSetMappingand @NamedNativeQueryを使用しEntityます。

最初の2つのメソッドは、カスタマイズされたコンバーターなど、多くの中間ハンドラーを呼び出します。たとえば、をAntiStealing定義するとsecretKey、永続化する前に、暗号化するためにコンバーターが挿入されます。これにより、最初の2つのメソッドは変換したものを返しますが、secretKeyこれは私が望んでいるものではありません。方法3はコンバーターに打ち勝ち、返さsecretKeyれる値は保存されているものと同じです(暗号化されたもの)。


方法1は実際にはSpringを必要とせず、純粋なHibernateで動作します。
Martin Vysny、2017年

@MartinVysnyはい、M1はJPQLです。JPQLを実装するプロジェクトは、それをサポートする必要があります。このように、M2も広くサポートされていますか?
Tiina、2017年

8

アンラップ手順を実行して、エンティティ(Beans / POJO)以外に結果を割り当てることができます。手順は以下の通りです。

List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
        .setParameter("userId", userId)
        .unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();

使用方法は、JPA-Hibernate実装用です。


JobDTOデフォルトのコンストラクタが必要であることに注意してください。または、AliasToBeanResultTransformer実装に基づいて独自のトランスフォーマーを実装することもできます。
Lu55

4

最初に次の注釈を宣言します。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultEntity {
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultColumn {
    int index();
}

次に、次のようにPOJOに注釈を付けます。

@NativeQueryResultEntity
public class ClassX {
    @NativeQueryResultColumn(index=0)
    private String a;

    @NativeQueryResultColumn(index=1)
    private String b;
}

次に、注釈プロセッサを記述します。

public class NativeQueryResultsMapper {

    private static Logger log = LoggerFactory.getLogger(NativeQueryResultsMapper.class);

    public static <T> List<T> map(List<Object[]> objectArrayList, Class<T> genericType) {
        List<T> ret = new ArrayList<T>();
        List<Field> mappingFields = getNativeQueryResultColumnAnnotatedFields(genericType);
        try {
            for (Object[] objectArr : objectArrayList) {
                T t = genericType.newInstance();
                for (int i = 0; i < objectArr.length; i++) {
                    BeanUtils.setProperty(t, mappingFields.get(i).getName(), objectArr[i]);
                }
                ret.add(t);
            }
        } catch (InstantiationException ie) {
            log.debug("Cannot instantiate: ", ie);
            ret.clear();
        } catch (IllegalAccessException iae) {
            log.debug("Illegal access: ", iae);
            ret.clear();
        } catch (InvocationTargetException ite) {
            log.debug("Cannot invoke method: ", ite);
            ret.clear();
        }
        return ret;
    }

    // Get ordered list of fields
    private static <T> List<Field> getNativeQueryResultColumnAnnotatedFields(Class<T> genericType) {
        Field[] fields = genericType.getDeclaredFields();
        List<Field> orderedFields = Arrays.asList(new Field[fields.length]);
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].isAnnotationPresent(NativeQueryResultColumn.class)) {
                NativeQueryResultColumn nqrc = fields[i].getAnnotation(NativeQueryResultColumn.class);
                orderedFields.set(nqrc.index(), fields[i]);
            }
        }
        return orderedFields;
    }
}

上記のフレームワークを次のように使用します。

String sql = "select a,b from x order by a";
Query q = entityManager.createNativeQuery(sql);

List<ClassX> results = NativeQueryResultsMapper.map(q.getResultList(), ClassX.class);

どのパッケージがBeanUtils入っていますか?
ハリッシュ

4

最も簡単な方法は、投影法を使用することです。クエリ結果をインターフェイスに直接マップでき、SqlResultSetMappingを使用するよりも実装が簡単です。

以下に例を示します。

@Repository
public interface PeopleRepository extends JpaRepository<People, Long> {

    @Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
        "FROM people p INNER JOIN dream_people dp " +
        "ON p.id = dp.people_id " +
        "WHERE p.user_id = :userId " +
        "GROUP BY dp.people_id " +
        "ORDER BY p.name", nativeQuery = true)
    List<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId);

    @Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
        "FROM people p INNER JOIN dream_people dp " +
        "ON p.id = dp.people_id " +
        "WHERE p.user_id = :userId " +
        "GROUP BY dp.people_id " +
        "ORDER BY p.name", nativeQuery = true)
    Page<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId, Pageable pageable);

}



// Interface to which result is projected
public interface PeopleDTO {

    String getName();

    Long getCount();

}

投影されたインターフェイスのフィールドは、このエンティティのフィールドと一致する必要があります。そうしないと、フィールドマッピングが壊れる可能性があります。

また、SELECT table.column表記法を使用する場合は、例に示すように、エンティティの名前に一致するエイリアスを常に定義してください。


1
ネイティブクエリとプロジェクションはうまく機能しません。
Kevin Rave

1
私はかなり右の仕事へのフィールドのマッピングを取得できませんでした-ほとんどの値がnullとして戻ってくる保持
ayang

4

hibernateでは、このコードを使用してネイティブクエリを簡単にマップできます。

private List < Map < String, Object >> getNativeQueryResultInMap() {
String mapQueryStr = "SELECT * FROM AB_SERVICE three ";
Query query = em.createNativeQuery(mapQueryStr);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List < Map < String, Object >> result = query.getResultList();
for (Map map: result) {
    System.out.println("after request  ::: " + map);
}
return result;}

2

Hibernateの使用:

@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = repo.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u").addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE).addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE).addScalar("loginType", IntegerType.INSTANCE)
        .setResultTransformer(Transformers.aliasToBean(User2DTO.class));

    List<User2DTO> userList = q.list();
}

2

ResultSetを使用した古いスタイル

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = this.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection con) throws SQLException {
            try (PreparedStatement stmt = con.prepareStatement(
                    "SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
                ResultSet rs = stmt.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
                }
                System.out.println("");
                while (rs.next()) {
                    System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
                }
            }
        }
    });
}

1

他の人がすでにすべての可能な解決策を述べているので、私は私の回避策解決策を共有しています。

との私の状況でPostgres 9.4Jackson

//Convert it to named native query.
List<String> list = em.createNativeQuery("select cast(array_to_json(array_agg(row_to_json(a))) as text) from myschema.actors a")
                   .getResultList();

List<ActorProxy> map = new ObjectMapper().readValue(list.get(0), new TypeReference<List<ActorProxy>>() {});

他のデータベースでも同じことがわかると思います。

また、参考として、マップとしてのJPA 2.0ネイティブクエリ結果


1

これがここに当てはまるかどうかはわかりませんが、同様の質問があり、次の簡単な解決策/例を見つけました:

private EntityManager entityManager;
...
    final String sql = " SELECT * FROM STORE "; // select from the table STORE
    final Query sqlQuery = entityManager.createNativeQuery(sql, Store.class);

    @SuppressWarnings("unchecked")
    List<Store> results = (List<Store>) sqlQuery.getResultList();

私の場合、Stringsで定義されたSQLパーツを別の場所で使用する必要があったため、NamedNativeQueryを使用するだけでは不十分でした。


エンティティを返すとすぐに。派手なものは何もない。問題は、結果をアンマネージPOJOにマップしようとするときです。
Olgun Kaya

1

結果セットを使用した古いスタイル

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = this.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection con) throws SQLException {
            try (PreparedStatement stmt = con.prepareStatement(
                    "SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
                ResultSet rs = stmt.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
                }
                System.out.println("");
                while (rs.next()) {
                    System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
                }
            }
        }
    });
}

1

次の方法で問題を解決しました:

   //Add actual table name here in Query
    final String sqlQuery = "Select a.* from ACTORS a"
    // add your entity manager here 
    Query query = entityManager.createNativeQuery(sqlQuery,Actors.class);
    //List contains the mapped entity data.
    List<Actors> list = (List<Actors>) query.getResultList();

0

POJOを擬似エンティティとして使用して、複雑なSqlResultSetMappingを使用せずにネイティブクエリから結果を取得する方法については、以下の例を参照してください。POJOに2つの注釈、つまり@Enityとダミーの@Idが必要です。@Idは任意のフィールドで使用できます。@ Idフィールドには重複するキーを含めることができますが、null値を含めることはできません。

@Enityは物理テーブルにマップしないため、このPOJOは疑似エンティティと呼ばれます。

環境:eclipselink 2.5.0-RC1、jpa-2.1.0、mysql-connector-java-5.1.14

ここで完全なmavenプロジェクトをダウンロードできます

ネイティブクエリはmysqlサンプルの従業員db http://dev.mysql.com/doc/employee/en/employees-installation.htmlに基づいてい ます

persistence.xml

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="jpa-mysql" transaction-type="RESOURCE_LOCAL">
    <class>org.moonwave.jpa.model.pojo.Employee</class>
    <properties>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/employees" />
        <property name="javax.persistence.jdbc.user" value="user" />
        <property name="javax.persistence.jdbc.password" value="***" />
        <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
    </properties>
</persistence-unit>

Employee.java

package org.moonwave.jpa.model.pojo;

@Entity
public class Employee {

@Id
protected Long empNo;

protected String firstName;
protected String lastName;
protected String title;

public Long getEmpNo() {
    return empNo;
}
public void setEmpNo(Long empNo) {
    this.empNo = empNo;
}
public String getFirstName() {
    return firstName;
}
public void setFirstName(String firstName) {
    this.firstName = firstName;
}
public String getLastName() {
    return lastName;
}
public void setLastName(String lastName) {
    this.lastName = lastName;
}   
public String getTitle() {
    return title;
}
public void setTitle(String title) {
    this.title = title;
}
public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("empNo: ").append(empNo);
    sb.append(", firstName: ").append(firstName);
    sb.append(", lastName: ").append(lastName);
    sb.append(", title: ").append(title);
    return sb.toString();
}
}

EmployeeNativeQuery.java

public class EmployeeNativeQuery {
private EntityManager em;
private EntityManagerFactory emf;

public void setUp() throws Exception {
    emf=Persistence.createEntityManagerFactory("jpa-mysql");
    em=emf.createEntityManager();
}
public void tearDown()throws Exception {
    em.close();
    emf.close();
}

@SuppressWarnings("unchecked")
public void query() {
    Query query = em.createNativeQuery("select e.emp_no as empNo, e.first_name as firstName, e.last_name as lastName," + 
            "t.title from employees e join titles t on e.emp_no = t.emp_no", Employee.class);
    query.setMaxResults(30);
    List<Employee> list = (List<Employee>) query.getResultList();
    int i = 0;
    for (Object emp : list) {
        System.out.println(++i + ": " + emp.toString());
    }
}

public static void main( String[] args ) {
    EmployeeNativeQuery test = new EmployeeNativeQuery();
    try {
        test.setUp();
        test.query();
        test.tearDown();
    } catch (Exception e) {
        System.out.println(e);
    }
}
}

1
あなたのlist主張によると、Employeeあなたのfor-eachループが型を繰り返しているのはなぜObjectですか?次のようにfor-eachループを作成するfor(Employee emp : list)と、答えが間違っていて、リストの内容が従業員ではなく、抑制した警告がこの潜在的な間違いについて警告する目的であったことがわかります。
Edwin Dalorzo、2015年

@SuppressWarnings( "unchecked")はList<Employee> list = (List<Employee>) query.getResultList();Change for (Object emp : list)toの警告を抑制するために使用され ますが、リストがのインスタンスであるためfor (Employee emp : list)、保持されObject empてもエラーは発生しませんList<Employee>。私はgitプロジェクトのコードを変更しましたが、元の投稿に関連するコメントを残すためにここでは変更しませんでした
Jonathan L

問題は、クエリが雇用者のリストではなく、オブジェクトの配列を返すことです。あなたの抑制された警告はそれを隠しています。それらのいずれかを従業員に変換しようとする瞬間に、エラー、キャスト例外が発生します。
Edwin Dalorzo、2015年

Query query = em.createNativeQuery("select * ...", Employee.class);persistence.xmlを見ると、ネイティブクエリはEmployeeのリストを返します。問題なくプロジェクトをチェックアウトして実行しました。mysqlサンプルのemployees dbをローカルでセットアップすると、プロジェクトも実行できるはずです
Jonathan L

ああ、今あなたが何を意味しているのか分かります。しかし、その場合、これは通常のPOJOをターゲットオブジェクトとして使用することであり、あなたの答えはEmployee私がエンティティであると想定しているものを使用しているため、あなたの答えは質問を満たしません。だよね?
Edwin Dalorzo、2015年

0

Springを使用している場合は、 org.springframework.jdbc.core.RowMapper

次に例を示します。

public List query(String objectType, String namedQuery)
{
  String rowMapper = objectType + "RowMapper";
  // then by reflection you can instantiate and use. The RowMapper classes need to follow the naming specific convention to follow such implementation.
} 

0

Hibernateの使用:

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = repo.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")
        .addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE)
        .addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE)
        .addScalar("loginType", IntegerType.INSTANCE)
        .setResultTransformer(Transformers.aliasToBean(User2DTO.class));

    List<User2DTO> userList = q.list();
}

-1

SQLクエリをPOJOクラスコレクションに変換する簡単な方法

Query query = getCurrentSession().createSQLQuery(sqlQuery).addEntity(Actors.class);
List<Actors> list = (List<Actors>) query.list();
return list;

-1

必要なのは、コンストラクター付きのDTOだけです。

public class User2DTO implements Serializable {

    /** pode ser email ou id do Google comecando com G ou F para Facebook */
    private String username;

    private String password;

    private String email;

    private String name;

    private Integer loginType;

    public User2DTO(Object...fields) {
        super();
        this.username = (String) fields[0];
        this.name = (String) fields[1];
        this.email = (String) fields[2];
        this.password = (String) fields[3];
        this.loginType = (Integer) fields[4];
    }

そしてそれを呼び出す:

EntityManager em = repo.getEntityManager();
        Query q = em.createNativeQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u");
        List<Object[]> objList = q.getResultList();
        List<User2DTO> ooBj = objList.stream().map(User2DTO::new).collect(Collectors.toList());

新しい列を追加すると、コードが壊れます。
ディッシュ

-2

を使用しDTO Design Patternます。で使用されましたEJB 2.0。エンティティはコンテナ管理されました。DTO Design Patternこの問題を解決するために使用されます。アプリケーションが開発されている場合でも、それは、今使用されるかもしれないServer Sideし、Client Side別に。アノテーション付きでパス/リターンしたくないDTO場合に使用されますServer sideEntityClient Sideます。

DTOの例:

PersonEntity.java

@Entity
public class PersonEntity {
    @Id
    private String id;
    private String address;

    public PersonEntity(){

    }
    public PersonEntity(String id, String address) {
        this.id = id;
        this.address = address;
    }
    //getter and setter

}

PersonDTO.java

public class PersonDTO {
    private String id;
    private String address;

    public PersonDTO() {
    }
    public PersonDTO(String id, String address) {
        this.id = id;
        this.address = address;
    }

    //getter and setter 
}

DTOBuilder.java

public class DTOBuilder() {
    public static PersonDTO buildPersonDTO(PersonEntity person) {
        return new PersonDTO(person.getId(). person.getAddress());
    }
}

EntityBuilder.java <-それは必要です

public class EntityBuilder() {
    public static PersonEntity buildPersonEntity(PersonDTO person) {
        return new PersonEntity(person.getId(). person.getAddress());
    }
}

4
回答ありがとうございます。ここでは、DTOパターンは必要ありません。私の要件は、注釈の詳細をクライアントから隠すことではありません。そのため、アプリでPOJOをもう1つ作成する必要はありません。私の要件は、JAVAエンティティではなく、結果セット列と同じフィールドを持つ単純なPOJOクラスであるqa pojoに結果セットをキャストすることです。
Gunjan Shah 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.