JPA: Cách chuyển đổi kết quả truy vấn gốc được đặt thành bộ sưu tập lớp POJO


174

Tôi đang sử dụng JPA trong dự án của tôi.

Tôi đã đến một truy vấn trong đó tôi cần thực hiện thao tác nối trên năm bảng. Vì vậy, tôi đã tạo một truy vấn gốc trả về năm trường.

Bây giờ tôi muốn chuyển đổi đối tượng kết quả thành lớp java POJO có chứa năm Chuỗi giống nhau.

Có cách nào trong JPA để truyền trực tiếp kết quả đó vào danh sách đối tượng POJO không ??

Tôi đã đi đến giải pháp sau đây ..

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

Bây giờ ở đây trong resultClass, chúng ta có cần cung cấp một lớp thực thể JPA không? HOẶC Chúng ta có thể chuyển đổi nó thành bất kỳ lớp JAVA POJO nào có cùng tên cột không?


Kiểm tra câu trả lời này. Nó có câu trả lời hoàn chỉnh: stackoverflow.com/a/50365522/3073945
Md. Sajedul Karim

anh ấy đang sử dụng jpa, không phải mùa xuân
anh ấy vào

Câu trả lời:


103

JPA cung cấp một SqlResultSetMappingcho phép bạn ánh xạ bất kỳ trả về nào từ truy vấn gốc của bạn vào một Thực thểhoặc một lớp tùy chỉnh.

EDIT JPA 1.0 không cho phép ánh xạ tới các lớp không thực thể. Chỉ trong JPA 2.1, một ConstructorResult đã được thêm vào ánh xạ các giá trị trả về của một lớp java.

Ngoài ra, đối với vấn đề của OP với việc đếm, nó đủ để xác định ánh xạ tập kết quả với một lần duy nhất ColumnResult


1
Cảm ơn vi đa trả lơi. Ở đây chúng tôi đang ánh xạ kết quả của chúng tôi với thực thể với lớp thực thể java với các chú thích "@EntityResult" và "@FieldResult". Tốt rồi. Nhưng ở đây tôi cần rõ ràng hơn. Được yêu cầu rằng lớp mà chúng ta đang ánh xạ với kết quả phải là lớp thực thể JPA? HOẶC chúng ta có thể sử dụng một lớp POJO đơn giản không phải là một thực thể mua có tất cả các biến cần thiết như các cột trong tập kết quả.
Gunjan Shah

1
@GunjanShah: cách tốt nhất để biết là hãy dùng thử :) Ngoài ra, một thực thể chỉ là cùng một pojo, chỉ với một số chú thích. miễn là bạn không cố gắng duy trì nó, nó sẽ ở lại một pojo.
Denis Tulskiy

2
Khi tôi thử điều này, tôi đã gặp một lỗi rằng lớp không phải là một Thực thể đã biết. Tôi đã kết thúc bằng cách sử dụng phương pháp stackoverflow.com/questions/5024533/ này thay vì cố gắng sử dụng truy vấn gốc.
FGreg

2
@EdwinDalorzo: phù hợp với jpa 1.0. trong jpa 2.1, họ đã thêm ConstructorResultmột trong các tham số SqlResultSetMappingcho phép sử dụng pojo với tất cả các trường được đặt trong hàm tạo. Tôi sẽ cập nhật câu trả lời.
Denis Tulskiy

4
Tôi thấy một sự thật cay đắng khác: Con constructorResult có thể ánh xạ tới POJO .. NHƯNG Con constructorResult phải ở trong lớp Entity để Entity bạn không thể tránh ... và do đó, thực tế khó khăn hơn: bạn cần một kết quả không quan tâm đến khóa chính - bạn vẫn phải có @Id trong Thực thể ... thật lố bịch phải không?
Arnab Dutta

210

Tôi đã tìm thấy một vài giải pháp cho việc này.

Sử dụng các thực thể đã ánh xạ (JPA 2.0)

Sử dụng JPA 2.0, không thể ánh xạ truy vấn gốc tới POJO, chỉ có thể được thực hiện với một thực thể.

Ví dụ:

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

Nhưng trong trường hợp này Jedi, phải là một lớp thực thể được ánh xạ.

Một cách khác để tránh cảnh báo không được kiểm tra ở đây, sẽ là sử dụng truy vấn gốc có tên. Vì vậy, nếu chúng ta khai báo truy vấn gốc trong một thực thể

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

Sau đó, chúng ta có thể chỉ cần làm:

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

Điều này an toàn hơn, nhưng chúng tôi vẫn bị hạn chế sử dụng thực thể được ánh xạ.

Ánh xạ thủ công

Một giải pháp tôi đã thử nghiệm một chút (trước khi JPA 2.1 xuất hiện) là thực hiện ánh xạ dựa trên hàm tạo POJO bằng một chút phản xạ.

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

Phương thức này về cơ bản lấy một mảng tuple (như được trả về bởi các truy vấn gốc) và ánh xạ nó tới một lớp POJO được cung cấp bằng cách tìm kiếm một hàm tạo có cùng số lượng trường và cùng loại.

Sau đó, chúng ta có thể sử dụng các phương pháp thuận tiện như:

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

Và chúng ta chỉ cần sử dụng kỹ thuật này như sau:

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

JPA 2.1 với @SqlResultSetMapping

Với sự xuất hiện của JPA 2.1, chúng ta có thể sử dụng chú thích @SqlResultSetMapping để giải quyết vấn đề.

Chúng ta cần khai báo ánh xạ tập kết quả ở đâu đó trong một thực thể:

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

Và sau đó chúng tôi chỉ cần làm:

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

Tất nhiên, trong trường hợp này Jedikhông cần phải là một thực thể được ánh xạ. Nó có thể là một POJO thông thường.

Sử dụng ánh xạ XML

Tôi là một trong những người tìm thấy thêm tất cả những điều này @SqlResultSetMappingkhá xâm lấn vào các thực thể của mình và tôi đặc biệt không thích định nghĩa của các truy vấn được đặt tên trong các thực thể, vì vậy, thay vào đó tôi làm tất cả điều này trong META-INF/orm.xmltệp:

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

Và đó là tất cả những giải pháp tôi biết. Hai cách cuối cùng là cách lý tưởng nếu chúng ta có thể sử dụng JPA 2.1.


1
Sidenote: Tôi chỉ sử dụng cách tiếp cận JPA 2.0 với sự phụ thuộc JPA2.1, và nó đã thất bại. Vì vậy, có lẽ điều này không tương thích xuống ...
viên

1
bạn có ý nghĩa gì bởi "một nơi nào đó trong một thực thể"? Pojo của tôi không phải là Thực thể JPA Tôi có thể khai báo @SqlResultSetMapping trong POJO của tôi không? Tôi quan tâm đến các giải pháp JPA 2.1. Xin hãy chính xác hơn một chút.
Alboz

3
@Alboz @SqlResultSetMappingPhải đặt trong một thực thể vì đó là những gì JPA sẽ đọc siêu dữ liệu từ đó. Bạn không thể mong đợi JPA kiểm tra POJO của bạn. Thực thể mà bạn đặt ánh xạ là không liên quan, có lẽ là thực thể có liên quan nhiều hơn đến kết quả POJO của bạn. Ngoài ra, ánh xạ có thể được biểu thị bằng XML để tránh khớp nối với một thực thể hoàn toàn không liên quan.
Edwin Dalorzo

1
Có thể cho construcorresult sử dụng một lớp có một lớp lồng nhau?
chrismarx

5
Nếu sử dụng JPA 2.1 với @SqlResultSetMappingđiều đáng chú ý là Jedilớp sẽ yêu cầu một hàm tạo all-arg và @ColumnResultchú thích có thể cần typethuộc tính được thêm vào các chuyển đổi có thể không ẩn (tôi cần thêm type = ZonedDateTime.classmột số cột).
Glenn

11

Có, với JPA 2.1 thật dễ dàng. Bạn có chú thích rất hữu ích. Họ đơn giản hóa cuộc sống của bạn.

Trước tiên, khai báo truy vấn gốc của bạn, sau đó ánh xạ tập kết quả của bạn (xác định ánh xạ dữ liệu được cơ sở dữ liệu trả về POJO của bạn). Viết lớp POJO của bạn để tham khảo (không bao gồm ở đây cho ngắn gọn). Cuối cùng nhưng không kém phần quan trọng: tạo một phương thức trong DAO (ví dụ) để gọi truy vấn. Điều này làm việc cho tôi trong một ứng dụng dropwizard (1.0.0).

Đầu tiên khai báo một truy vấn gốc trong một lớp thực thể:

@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

Bên dưới bạn có thể thêm khai báo ánh xạ kết quả:

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

Sau này trong DAO, bạn có thể tham khảo truy vấn như

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

Đó là nó.


Câu trả lời hay, nhưng tôi nghĩ rằng bạn đã bỏ lỡ dấu ngoặc đơn sau chú thích @ColumnResult đầu tiên.
mwatzer

Có những lỗi trong mã, nhưng dễ sửa. Ví dụ: "resulSetMapping =" nên là "resultsetMapping ="
Zbyszek

3
Tôi thấy một sự thật cay đắng khác: NamedNativeQuery & SqlResultSetMapping phải ở trong lớp @Entity
Arnab Dutta

10

Nếu bạn sử dụng Spring-jpa, đây là một bổ sung cho câu trả lời và câu hỏi này. Vui lòng sửa lỗi này nếu có sai sót. Tôi chủ yếu sử dụng ba phương pháp để đạt được "kết quả ánh xạ Object[]tới pojo" dựa trên nhu cầu thực tế mà tôi đáp ứng:

  1. JPA được xây dựng trong phương pháp là đủ.
  2. JPA được xây dựng trong phương thức là không đủ, nhưng một tùy chỉnh sqlvới nó Entitylà đủ.
  3. 2 cái trước thất bại, và tôi phải sử dụng a nativeQuery. Dưới đây là những ví dụ. Các pojo dự kiến:

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

Phương pháp 1 : Thay đổi pojo thành giao diện:

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

Và kho lưu trữ:

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

Cách 2 : Kho lưu trữ:

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

Lưu ý: chuỗi tham số của hàm tạo POJO phải giống hệt nhau trong cả định nghĩa POJO và sql.

Phương pháp 3 : Sử dụng @SqlResultSetMapping@NamedNativeQuerytrong Entitynhư ví dụ trong câu trả lời Edwin Dalorzo của.

Hai phương thức đầu tiên sẽ gọi nhiều trình xử lý ở giữa, như bộ chuyển đổi tùy chỉnh. Ví dụ, AntiStealingđịnh nghĩa a secretKey, trước khi nó được duy trì, một trình chuyển đổi được chèn để mã hóa nó. Điều này sẽ dẫn đến 2 phương thức đầu tiên trả lại một chuyển đổi trở lại secretKeykhông phải là điều tôi muốn. Mặc dù phương thức 3 sẽ khắc phục được trình chuyển đổi và được trả về secretKeysẽ giống như được lưu trữ (một phương thức được mã hóa).


Phương pháp 1 không thực sự yêu cầu Spring và hoạt động với Hibernate thuần túy.
Martin Vysny

@MartinVysny có, M1 là JPQL. bất kỳ dự án nào thực hiện JPQL nên hỗ trợ nó. Bằng cách này, có lẽ M2 cũng được hỗ trợ rộng rãi?
Tiina

8

Thủ tục unwrap có thể được thực hiện để gán kết quả cho phi thực thể (đó là Đậu / POJO). Thủ tục như sau.

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

Việc sử dụng là để thực hiện JPA-Hibernate.


lưu ý rằng JobDTOnên có constructor mặc định. Hoặc bạn có thể thực hiện máy biến áp của riêng bạn dựa trên AliasToBeanResultTransformerviệc thực hiện.
Lu55

4

Đầu tiên khai báo các chú thích sau:

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

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

Sau đó chú thích POJO của bạn như sau:

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

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

Sau đó viết bộ xử lý chú thích:

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

Sử dụng khung trên như sau:

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

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

Gói nào BeanUtilstrong?
Harish

4

Cách dễ nhất là sử dụng các phép chiếu như vậy . Nó có thể ánh xạ kết quả truy vấn trực tiếp đến các giao diện và dễ thực hiện hơn so với sử dụng SqlResultSetMapping.

Một ví dụ đã được biểu diễn ở dưới:

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

}

Các trường từ giao diện được chiếu phải khớp với các trường trong thực thể này. Nếu không thì ánh xạ trường có thể bị phá vỡ.

Ngoài ra nếu bạn sử dụng SELECT table.columnký hiệu luôn xác định bí danh khớp với tên từ thực thể như trong ví dụ.


1
truy vấn và dự đoán riêng không đi cùng nhau.
Kevin Rave

1
Tôi không thể làm cho ánh xạ trường hoạt động hoàn toàn đúng - hầu hết các giá trị tiếp tục trở lại là null
ayang

4

Trong chế độ ngủ đông, bạn có thể sử dụng mã này để dễ dàng ánh xạ truy vấn gốc của mình.

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

Sử dụng 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

Kiểu cũ sử dụng Kết quả

@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

Vì những người khác đã đề cập đến tất cả các giải pháp có thể, tôi đang chia sẻ giải pháp khắc phục của tôi.

Trong tình huống của tôi với Postgres 9.4, trong khi làm việc với 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>>() {});

Tôi chắc chắn bạn có thể tìm thấy tương tự cho các cơ sở dữ liệu khác.

Ngoài ra FYI, JPA 2.0 kết quả truy vấn gốc như bản đồ


1

Không chắc chắn nếu điều này phù hợp ở đây, nhưng tôi đã có câu hỏi tương tự và tìm thấy sau đây giải pháp / ví dụ đơn giản cho tôi:

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

Trong trường hợp của tôi, tôi đã phải sử dụng các phần SQL được xác định trong Chuỗi ở một nơi khác, vì vậy tôi không thể chỉ sử dụng NamedNativeQuery.


ngay khi chúng tôi trở lại thực thể. không có gì lạ mắt. vấn đề là khi bạn cố gắng ánh xạ kết quả đến một POJO không được quản lý.
Olgun Kaya

1

Kiểu cũ sử dụng 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

Chúng tôi đã giải quyết vấn đề bằng cách sử dụng theo cách sau:

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

Xem ví dụ dưới đây để sử dụng POJO làm thực thể giả để truy xuất kết quả từ truy vấn gốc mà không sử dụng SqlResultSetMapping phức tạp. Chỉ cần hai chú thích, một @Enity trần và một @Id giả trong POJO của bạn. @Id có thể được sử dụng trên bất kỳ trường nào bạn chọn, trường @Id có thể có các khóa trùng lặp nhưng không có giá trị null.

Vì @Enity không ánh xạ tới bất kỳ bảng vật lý nào, nên POJO này được gọi là thực thể giả.

Môi trường: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-Connector-java-5.1.14

Bạn có thể tải về dự án maven hoàn chỉnh tại đây

Truy vấn gốc được dựa trên nhân viên mẫu mysql db http://dev.mysql.com/doc/employee/en/empologists-installation.html

kiên trì

<?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, được cho là của bạn , một danh sách Employee, tại sao vòng lặp for của bạn lặp lại qua một loại Object? Nếu bạn viết cho mỗi vòng lặp của mình for(Employee emp : list)thì bạn sẽ phát hiện ra rằng câu trả lời của bạn sai và nội dung trong danh sách của bạn không phải là nhân viên và cảnh báo mà bạn đã đàn áp có mục đích cảnh báo bạn về sai lầm tiềm ẩn này.
Edwin Dalorzo

@SuppressWarnings ("không được kiểm tra") được sử dụng để chặn cảnh báo cho List<Employee> list = (List<Employee>) query.getResultList();Thay đổi for (Object emp : list)thành for (Employee emp : list)tốt hơn, nhưng không có lỗi nếu được giữ Object empvì danh sách là một ví dụ của List<Employee>. Tôi đã thay đổi mã trong dự án git nhưng không ở đây để giữ bình luận của bạn có liên quan đến bài viết gốc
Jonathan L

vấn đề là truy vấn của bạn không trả về danh sách các nhà tuyển dụng, mà là một mảng các đối tượng. Cảnh báo bị đàn áp của bạn đang che giấu điều đó. Trong thời điểm mà bạn cố gắng chuyển đổi bất kỳ ai trong số đó thành nhân viên, bạn sẽ gặp lỗi, ngoại lệ.
Edwin Dalorzo 23/1/2015

Nhìn vào Query query = em.createNativeQuery("select * ...", Employee.class);và kiên trì, truy vấn gốc sẽ trả về một danh sách Nhân viên. Tôi chỉ kiểm tra và chạy dự án w / o vấn đề. Nếu bạn thiết lập nhân viên mẫu mysql db cục bộ, bạn cũng có thể chạy dự án
Jonathan L

Oh tôi hiểu ý của bạn bây giờ. Nhưng trong trường hợp đó, câu trả lời của bạn không thỏa mãn câu hỏi, bởi vì đây là về việc sử dụng POJO thông thường làm đối tượng mục tiêu và câu trả lời của bạn đang sử dụng Employeemà tôi giả sử là một thực thể. Phải không?
Edwin Dalorzo

0

nếu bạn đang sử dụng Spring, bạn có thể sử dụng org.springframework.jdbc.core.RowMapper

Đây là một ví dụ:

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

Sử dụng 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

Cách đơn giản để chuyển đổi truy vấn SQL sang bộ sưu tập lớp POJO,

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

-1

Tất cả những gì bạn cần là một DTO với hàm tạo:

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

và gọi nó là:

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

Thêm một cột mới và mã sẽ phá vỡ.
Món ăn

-2

Sử dụng DTO Design Pattern. Nó đã được sử dụng trong EJB 2.0. Thực thể được quản lý container. DTO Design Patternđược sử dụng để giải quyết vấn đề này. Nhưng, nó có thể được sử dụng ngay bây giờ, khi ứng dụng được phát triển Server SideClient Sidetách biệt. DTOđược sử dụng khi Server sidekhông muốn vượt qua / trả lại Entityvới chú thích Client Side.

Ví dụ về 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 <- nó cần

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

4
Cảm ơn câu trả lời. Ở đây tôi không cần mẫu DTO. Yêu cầu của tôi là không che giấu các chi tiết chú thích từ khách hàng. Vì vậy, tôi không cần tạo thêm một POJO trong ứng dụng của mình. Yêu cầu của tôi là truyền tập kết quả thành qa pojo không phải là một thực thể JAVA mà là lớp POJO đơn giản có cùng các trường như các cột tập kết quả.
Gunjan Shah
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.