JPA: Yerel sorgu sonuç kümesini POJO sınıfı koleksiyonuna dönüştürme


174

Projemde JPA kullanıyorum.

Beş tablo üzerinde birleştirme işlemi yapmak için gereken bir sorgu geldi. Bu yüzden beş alan döndüren yerel bir sorgu oluşturdum.

Şimdi sonuç nesnesini aynı beş dizeyi içeren java POJO sınıfına dönüştürmek istiyorum.

JPA doğrudan POJO nesne listesine bu sonucu döküm herhangi bir yolu var mı?

Aşağıdaki çözüme geldim ..

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

Şimdi burada resultsClass, gerçek JPA varlığı olan bir sınıf sağlamamız gerekiyor mu? VEYA Aynı sütun adlarını içeren herhangi bir JAVA POJO sınıfına dönüştürebilir miyiz?


Bu cevabı kontrol edin. Tam cevabı var: stackoverflow.com/a/50365522/3073945
Md. Sajedul Karim

o JPA kullanıyor, değil bahar
Onu

Yanıtlar:


103

JPA, SqlResultSetMapping yerel sorgunuzdan dönenleri bir Varlığa eşlemenize olanak tanıyan birveya özel bir sınıf.

JPA 1.0 DÜZENLE varlık olmayan sınıflarla eşleştirmeye izin vermez. Yalnızca JPA 2.1'de bir java sınıfının dönüş değerlerini eşlemek için bir ConstructorResult eklenmiştir.

Ayrıca OP'nin sayma problemi için bir sonuç kümesi eşlemesi tanımlamak yeterli olmalıdır. ColumnResult


1
Cevap için teşekkürler. Burada, sonucumuzu "#EntityResult" ve "@FieldResult" ek açıklamalarıyla bu java varlık sınıfına sahip varlık ile eşleştiriyoruz. Bu iyi. Ama burada daha fazla açıklığa ihtiyacım var. Sonuçla eşleştirdiğimiz sınıfın bir JPA varlık sınıfı olması gerekir mi? VEYA sonuç kümesindeki sütunlar için gerekli değişkenlerin tümüne sahip olan bir varlık satın alma işlemi olmayan basit bir POJO sınıfı kullanabilir miyiz.
Gunjan Shah

1
@GunjanShah: Bilmenin en iyi yolu denemektir :) ayrıca, bir varlık sadece bazı ek açıklamalarla aynı pojo'dur. Eğer ısrar etmeye çalışmadığınız sürece, bir pojo kalacaktır.
Denis Tulskiy

2
Bunu denediğimde sınıfın bilinen bir Varlık olmadığını gösteren bir hata aldım. Yerel bir sorgu kullanmaya çalışmak yerine stackoverflow.com/questions/5024533/… bu yaklaşımı kullanarak sona erdi .
FGreg

2
@EdwinDalorzo: jpa 1.0 için doğru. jpa 2.1'de , yapıcıda ayarlanan tüm alanlarla bir pojo kullanılmasına izin veren ConstructorResultparametrelerden biri olarak eklediler SqlResultSetMapping. Cevabı güncelleyeceğim.
Denis Tulskiy

4
Başka bir acı gerçeği görüyorum: ConstructorResult bir POJO ile eşleştirebilir .. AMA ConstructorResult kendisi Varlık sınıfında olması gerekir, böylece Varlık önlemek ve böylece daha büyük zor gerçek: umurumda değil bazı sonuç gerekir birincil anahtara - hala Varlıkta @Id var ... saçma değil mi?
Arnab Dutta

210

Bunun için birkaç çözüm buldum.

Eşlenen Varlıkları Kullanma (JPA 2.0)

JPA 2.0 kullanarak, yerel bir sorguyu POJO ile eşlemek mümkün değildir, yalnızca bir varlık ile yapılabilir.

Örneğin:

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

Ancak bu durumda, Jedieşlenmiş bir varlık sınıfı olmalıdır.

Burada işaretlenmeyen uyarıyı önlemenin bir alternatifi, adlandırılmış bir yerel sorgu kullanmak olabilir. Dolayısıyla, bir varlıktaki yerel sorguyu bildirirsek

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

Ardından, şunları yapabiliriz:

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

Bu daha güvenlidir, ancak yine de eşlenmiş bir varlığı kullanmakla sınırlıdır.

Manuel Haritalama

Biraz denediğim bir çözüm (JPA 2.1 gelmeden önce) biraz yansıma kullanarak bir POJO yapıcısına eşleme yapıyordu.

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);
   }
}

Bu yöntem temel olarak bir tuple dizisi alır (yerel sorgular tarafından döndürüldüğü gibi) ve aynı sayıda alana ve aynı tipte bir yapıcı arayarak bunu sağlanan bir POJO sınıfıyla eşleştirir.

O zaman aşağıdaki gibi uygun yöntemleri kullanabiliriz:

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);
}

Ve bu tekniği aşağıdaki gibi kullanabiliriz:

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

@SqlResultSetMapping ile JPA 2.1

JPA 2.1'in gelişiyle, sorunu çözmek için @SqlResultSetMapping ek açıklamasını kullanabiliriz.

Bir varlığın herhangi bir yerinde bir sonuç kümesi eşlemesi bildirmemiz gerekir:

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

Ve sonra basitçe şunu yaparız:

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

Tabii ki, bu durumda Jedieşlenmiş bir varlık olması gerekmez. Düzenli bir POJO olabilir.

XML Eşlemeyi Kullanma

Varlıklarımdaki tüm bu @SqlResultSetMappingoldukça invaziv eklemeyi bulanlardan biriyim ve özellikle varlıklar içindeki adlandırılmış sorguların tanımını beğenmedim, bu yüzden alternatif olarak tüm bunları META-INF/orm.xmldosyada yapıyorum :

<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>

Ve bunların hepsi bildiğim çözümler. Son iki, JPA 2.1'i kullanabilmemiz için ideal yoldur.


1
Sidenote: JPA 2.0 yaklaşımını JPA2.1 bağımlılığı ile kullandım ve başarısız oldu. Yani muhtemelen bu aşağı doğru uyumlu değil ...
membersound

1
"bir varlıkta bir yerde" ile ne demek istiyorsun? Benim Pojo bir JPA Varlığı değil POJO @SqlResultSetMapping ilan edemez? JPA 2.1 çözümleriyle ilgileniyorum. Lütfen biraz daha kesin olun.
Alboz

3
@Alboz @SqlResultSetMappingBir varlığa yerleştirilmelidir, çünkü JPA meta verileri buradan okuyacaktır. JPA'nın POJO'larınızı incelemesini bekleyemezsiniz. Eşlemeyi yerleştirdiğiniz varlık, belki de POJO sonuçlarınızla daha alakalı olan alakasız. Alternatif olarak, tamamen alakasız bir varlık ile birleşmeyi önlemek için eşleme XML olarak ifade edilebilir.
Edwin Dalorzo

1
Yapıcı sonucunun iç içe bir sınıfa sahip bir sınıfı kullanması mümkün müdür?
chrismarx

5
JPA 2.1 ile birlikte kullanıldığında @SqlResultSetMapping, Jedisınıfın bir all-arg yapıcısı gerektireceğini ve @ColumnResultek açıklama, typeörtük olmayabilecek dönüşümlere eklenen niteliği gerektirebilir ( type = ZonedDateTime.classbazı sütunlar için eklemem gerekiyor ).
Glenn

11

Evet, JPA 2.1 ile çok kolay. Çok faydalı Ek Açıklamalarınız var. Hayatınızı basitleştirir.

Önce yerel sorgunuzu, ardından sonuç kümesi eşlemenizi (veritabanı tarafından döndürülen verilerin POJO'larınıza eşlenmesini tanımlayan) eşleştirin. Başvurmak için POJO sınıfınızı yazın (kısalık için buraya dahil değildir). Son fakat en önemlisi: sorguyu çağırmak için bir DAO'da (örneğin) bir yöntem oluşturun. Bu benim için bir dropwizard (1.0.0) uygulamasında çalıştı.

İlk olarak bir varlık sınıfında yerel bir sorgu bildirin:

@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

Altına sonuç kümesi eşleme bildirimini ekleyebilirsiniz:

@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)
          }
      )
   } 
)

Daha sonra bir DAO'da sorguyu

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

Bu kadar.


Güzel cevap, ama sanırım ilk @ColumnResult ek açıklamasından sonra parantezleri kaçırdınız.
mwatzer

Kodda hatalar var, ancak düzeltilmesi kolay. Örneğin: "resulSetMapping =", "sonuçSetMapping =" olmalıdır
Zbyszek

3
Başka acı bir gerçek görüyorum: NamedNativeQuery & SqlResultSetMapping bir @Entity sınıfında olmalı
Arnab Dutta

10

Kullanırsanız Spring-jpa, bu cevaplara ve bu soruya bir ektir. Herhangi bir kusur varsa lütfen bunu düzeltin. Ben Object[]esas olarak hangi pratik ihtiyacına göre "pojo eşleme sonucu" elde etmek için üç yöntem kullandım :

  1. JPA yerleşik yöntem yeterlidir.
  2. JPA yerleşik yöntem yeterli değildir, ancak sqlonunla özelleştirilmiş bir Entityyeterlidir.
  3. İlk 2 başarısız oldu ve a kullanmak zorundayım nativeQuery. İşte örnekler. Pojo bekleniyor:

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

Yöntem 1 : Pojo'yu bir arabirim olarak değiştirin:

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

Ve depo:

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

Yöntem 2 : Depo:

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

Not: POJO yapıcısının parametre sırası hem POJO tanımında hem de sql'de aynı olmalıdır.

Yöntem 3 : Kullanım @SqlResultSetMappingve @NamedNativeQueryiçinde EntityEdwin Dalorzo yanıtında örnek olarak.

İlk iki yöntem, özelleştirilmiş dönüştürücüler gibi birçok ortadaki işleyiciyi çağırır. Örneğin, AntiStealinga secretKey, kalıcı olmadan önce şifrelemek için bir dönüştürücü ekler. Bu ilk 2 yöntem secretKeyne istediğim değil dönüştürülmüş bir geri dönen sonuçlanır . Yöntem 3, dönüştürücünün üstesinden gelirken ve döndürülen secretKey, depolandığı ile aynı olur (şifreli olan).


Yöntem 1 aslında Bahar gerektirmez ve saf Hazırda Bekletme ile çalışır.
Martin Vysny

@MartinVysny evet, M1 JPQL'dir. JPQL uygulayan tüm projeler bunu desteklemelidir. Bu şekilde, belki M2 de yaygın olarak desteklenmektedir?
Tiina

8

Varlığı olmayanlara (Fasulye / POJO olan) sonuçları atamak için çözme prosedürü gerçekleştirilebilir. Prosedür aşağıdaki gibidir.

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

Kullanım JPA-Hibernate uygulaması içindir.


JobDTOvarsayılan yapıcı olması gerektiğini unutmayın . Veya uygulamaya dayalı olarak kendi transformatörünüzü AliasToBeanResultTransformeruygulayabilirsiniz.
Lu55

4

İlk önce aşağıdaki ek açıklamaları bildirin:

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

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

Ardından POJO'nuza aşağıdaki gibi açıklama ekleyin:

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

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

Ardından ek açıklama işlemcisi yazın:

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;
    }
}

Yukarıdaki çerçeveyi aşağıdaki gibi kullanın:

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

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

Hangi paketin BeanUtilsiçinde?
Harish

4

En kolay yol bu projeksiyonları kullanmaktır . Sorgu sonuçlarını doğrudan arabirimlere eşleyebilir ve uygulanması SqlResultSetMapping kullanmaktan daha kolaydır.

Aşağıda bir örnek gösterilmiştir:

@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();

}

Yansıtılan arabirimdeki alanların bu varlıktaki alanlarla eşleşmesi gerekir. Aksi takdirde alan eşleme bozulabilir.

Ayrıca SELECT table.columngösterim kullanıyorsanız, her zaman örnekte gösterildiği gibi varlıktan adlarla eşleşen diğer adları tanımlayın.


1
yerel sorgu ve projeksiyonlar birlikte iyi gitmez.
Kevin Rave

1
Alan eşlemesinin oldukça doğru çalışmasını sağlayamadım - çoğu değer null olarak geri dönmeye devam etti
ayang

4

Hazırda bekletme modunda, yerel sorgunuzu kolayca eşlemek için bu kodu kullanabilirsiniz.

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

Hazırda Bekletme özelliğini kullanma:

@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 kullanarak eski stil

@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

Diğerleri olası tüm çözümleri zaten belirttiğinden, geçici çözümümü paylaşıyorum.

Durumumda Postgres 9.4, ile çalışırken Jackson,

//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>>() {});

Eminim diğer veritabanları için de bulabilirsin.

Ayrıca harita olarak FYI, JPA 2.0 yerel sorgu sonuçları


1

Bunun buraya uyup uymadığından emin değilim, ancak benzer bir sorum vardı ve aşağıdaki basit çözümü / örneği buldum:

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();

Benim durumumda başka bir yerde Dizeleri tanımlanan SQL parçaları kullanmak zorunda kaldı, bu yüzden sadece NamedNativeQuery kullanmak olamazdı.


en kısa sürede biz dönen varlık. hiçbir şey fantezi. yönetilmeyen bir POJO sonucu eşlemeye çalıştığınızda sorunudur.
Olgun Kaya

1

Resultset kullanarak eski stil

@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

Sorunu aşağıdaki yolu kullanarak çözdük:

   //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

Karmaşık SqlResultSetMapping kullanmadan yerel sorgudan sonuç almak için sözde varlık olarak POJO kullanmak için aşağıdaki örneğe bakın. POJO'nuzda çıplak bir @Enity ve bir kukla @Id olmak üzere iki ek açıklamaya ihtiyacınız var. @Id istediğiniz herhangi bir alanda kullanılabilir, @Id alanı yinelenen anahtarlara sahip olabilir ancak boş değerlere sahip olamaz.

@Enity herhangi bir fiziksel tabloyla eşleşmediğinden, bu POJO'ya sözde varlık denir.

Çevre: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-konnektör-java-5.1.14

Sen tam maven projesi indirebilirsiniz burada

Yerel sorgu mysql örnek çalışanlarına dayanmaktadır 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
Senin bu yana listDİR, iddiaya göre, bir liste Employee, neden sizin için-her döngü bir tür yineleme edilir Object? Her döngü için olarak for(Employee emp : list)yazarsanız, cevabınızın yanlış olduğunu ve listenizdeki içeriğin çalışan olmadığını ve bastırdığınız uyarının sizi bu olası hata hakkında uyarma amacını bulduğunu keşfedersiniz.
Edwin Dalorzo

@SuppressWarnings ("unchecked"), List<Employee> list = (List<Employee>) query.getResultList();Change for (Object emp : list)to ( Değişiklik) uyarısını for (Employee emp : list)daha iyi bastırmak için kullanılır , ancak Object emplistenin bir örneği olduğu gibi tutulursa hata olmaz List<Employee>. Git projesindeki kodu değiştirdim, ancak yorumunuzu orijinal
Jonathan L

sorun, sorgunuzun bir istihdam listesi değil, bir dizi nesne döndürmesi. Bastırılmış uyarınız bunu gizliyor. Bunlardan herhangi birini bir çalışana dönüştürmeye çalıştığınız anlarda, bir hata, bir istisna alırsınız.
Edwin Dalorzo

Query query = em.createNativeQuery("select * ...", Employee.class);Ve persistence.xml dosyasına bakın , yerel sorgu bir Çalışan listesi döndürür. Ben sadece teslim ve proje w / o sorunu çalıştırın. MySQL örnek çalışanları db'yi yerel olarak
Jonathan L

Oh şimdi ne demek istediğini anlıyorum. Ama bu durumda cevabınız soruyu tatmin etmiyor, çünkü bu hedef nesne olarak normal bir POJO kullanmakla ilgiliydi ve cevabınız Employeebir varlık olduğunu düşündüğüm kullanıyor . Öyle değil mi?
Edwin Dalorzo

0

Spring kullanıyorsanız, org.springframework.jdbc.core.RowMapper

İşte bir örnek:

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

Hazırda Bekletme özelliğini kullanma:

@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 sorgusunu POJO sınıfı koleksiyonuna dönüştürmenin basit yolu,

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

-1

Tek ihtiyacınız bir kurucu ile bir 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];
    }

ve ara:

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());

Yeni bir sütun ekleyin ve kod kırılacaktır.
Çanak

-2

Kullanın DTO Design Pattern. İçinde kullanıldı EJB 2.0. Varlık konteyner tarafından yönetildi. DTO Design Patternbu sorunu çözmek için kullanılır. Ancak, uygulama geliştirildiğinde Server Sideve Client Sideayrı ayrı artık kullanılabilir . DTO, ek açıklama ile Server sideiletmek / geri dönmek istemediğinde kullanılır .EntityClient Side

DTO Örneği:

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 <- mide olması gerek

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

4
Cevabınız için teşekkürler. DTO kalıbına ihtiyacım yok. Benim gereksinimim, ek açıklama ayrıntılarını istemciden gizlememek. Bu yüzden uygulamamda bir tane daha POJO oluşturmam gerekmiyor. Benim gereksinimim sonuç kümesini bir JAVA varlığı değil, sonuç kümesi sütunları ile aynı alanlara sahip basit POJO sınıfı olan qa pojo'ya atamaktır.
Gunjan Shah
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.