2016-05-24 19 views
9

Theo dõi câu hỏi tôi đã đăng hôm qua: How to populate POJO class from custom Hibernate query?ánh xạ kết quả truy vấn Hibernate lên lớp tùy chỉnh?

Ai đó có thể chỉ cho tôi ví dụ về cách mã SQL sau trong Hibernate và nhận kết quả chính xác?

SQL:

select firstName, lastName 
from Employee 

Những gì tôi muốn làm, nếu nó có thể trong Hibernate, là để đưa các kết quả trong lớp cơ sở riêng của họ:

class Results { 
    private firstName; 
    private lastName; 
    // getters and setters 
} 

tôi tin rằng nó có thể ở JPA (sử dụng EntityManager), nhưng tôi đã không tìm ra cách để làm điều đó trong Hibernate (sử dụng SessionFactorySession).

Tôi đang cố gắng tìm hiểu Hibernate tốt hơn, và thậm chí truy vấn "đơn giản" này đang chứng minh nhầm lẫn để biết hình thức Hibernate trả về kết quả và cách ánh xạ kết quả vào lớp (cơ sở) của riêng tôi. Vì vậy, ở phần cuối của các thói quen DAO, tôi muốn làm:

List<Results> list = query.list(); 

trả lại một List của Results (lớp cơ sở của tôi).

Trả lời

7

Xem AliasToBeanResultTransformer:

quả biến áp cho phép chuyển đổi kết quả một cho một người dùng lớp được chỉ định đó sẽ được áp dụng thông qua phương thức setter hoặc các lĩnh vực phù hợp với những cái tên bí danh.

List resultWithAliasedBean = s.createCriteria(Enrolment.class) 
      .createAlias("student", "st") 
      .createAlias("course", "co") 
      .setProjection(Projections.projectionList() 
        .add(Projections.property("co.description"), "courseDescription") 
      ) 
      .setResultTransformer(new AliasToBeanResultTransformer(StudentDTO.class)) 
      .list(); 

StudentDTO dto = (StudentDTO)resultWithAliasedBean.get(0); 

mã sửa đổi của bạn:

List resultWithAliasedBean = s.createCriteria(Employee.class, "e") 
    .setProjection(Projections.projectionList() 
     .add(Projections.property("e.firstName"), "firstName") 
     .add(Projections.property("e.lastName"), "lastName") 
    ) 
    .setResultTransformer(new AliasToBeanResultTransformer(Results.class)) 
    .list(); 

Results dto = (Results) resultWithAliasedBean.get(0); 

Đối với các truy vấn SQL mẹ đẻ thấy Hibernate documentation:

13.1.5. Trả lại các thực thể không được quản lý

Có thể áp dụng một ResultTransformer cho các truy vấn SQL nguyên gốc, cho phép nó trả về các thực thể không được quản lý.

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS") 
    .setResultTransformer(Transformers.aliasToBean(CatDTO.class)) 

Truy vấn này quy định:

  • chuỗi truy vấn SQL
  • một biến kết quả Các truy vấn trên sẽ trả về một danh sách các CatDTO đã được khởi tạo và tiêm các giá trị của NAME và BIRTHNAME vào các thuộc tính hoặc trường tương ứng của nó.
+1

Funny, tôi có thể tạo lại vấn đề với Hibernate 5.1. Biến áp 'Transformers.aliasToBean (CatDTO.class)' không hoạt động. Tôi chỉ cần thử nghiệm với biến áp của riêng tôi trước khi :) Cố gắng tìm ra lý do tại sao như vậy. –

+1

Thật buồn cười, tôi đã đúng với các bí danh. Xem câu trả lời của tôi: http://stackoverflow.com/a/37423885/3405171 –

3

Bạn cần sử dụng hàm tạo và trong hql mới sử dụng.Tôi cho phép bạn ví dụ mã lấy từ câu hỏi này: hibernate HQL createQuery() list() type cast to model directly

class Result { 
    private firstName; 
    private lastName; 
    public Result (String firstName, String lastName){ 
     this.firstName = firstName; 
     this.lastName = lastName; 
    } 
} 

sau đó HQL của bạn

select new com.yourpackage.Result(employee.firstName,employee.lastName) 
from Employee 

và java của bạn (sử dụng Hibernate)

List<Result> results = session.createQuery("select new com.yourpackage.Result(employee.firstName,employee.lastName) from Employee").list(); 
8
select firstName, lastName from Employee 

query.setResultTransformer(Transformers.aliasToBean(MyResults.class)); 

Bạn có thể không sử dụng mã trên với Hibernate 5 và Hibernate 4 (ít nhất là Hibernate 4.3.6.Final), vì ngoại lệ

java.lang.ClassCastException: com.github.fluent.hibernate.request.persistent.UserDto cannot be cast to java.util.Map 
    at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102) 

Vấn đề là Hibernate chuyển đổi bí danh cho tên cột thành chữ thường - firstName trở thành FIRSTNAME. Và nó cố gắng tìm một getter với tên getFIRSTNAME(), và setter setFIRSTNAME() trong DTO sử dụng chiến lược như vậy

PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
      PropertyAccessStrategyBasicImpl.INSTANCE, 
      PropertyAccessStrategyFieldImpl.INSTANCE, 
      PropertyAccessStrategyMapImpl.INSTANCE 
    ); 

Chỉ PropertyAccessStrategyMapImpl.INSTANCE phù hợp, theo ý kiến ​​của Hibernate, tốt. Vì vậy, sau đó nó sẽ cố gắng chuyển đổi (Map)MyResults.

public void set(Object target, Object value, SessionFactoryImplementor factory) { 
    ((Map) target).put(propertyName, value); 
} 

Không biết, đó là lỗi hoặc tính năng.

Làm thế nào để giải quyết

Sử dụng bí danh với dấu ngoặc kép

public class Results { 

    private String firstName; 

    private String lastName; 

    public String getFirstName() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public void setFirstName(String firstName) { 
     this.firstName = firstName; 
    } 

    public void setLastName(String lastName) { 
     this.lastName = lastName; 
    } 

} 

String sql = "select firstName as \"firstName\", 
    lastName as \"lastName\" from Employee"; 

List<Results> employees = session.createSQLQuery(sql).setResultTransformer(
    Transformers.aliasToBean(Results.class)).list(); 

Sử dụng một máy biến áp kết quả tùy chỉnh

Một cách khác để giải quyết vấn đề - sử dụng một máy biến áp kết quả mà bỏ qua trường hợp tên phương thức (xử lý getFirstName()getFIRSTNAME()). Bạn có thể viết của riêng bạn hoặc sử dụng FluentHibernateResultTransformer. Bạn sẽ không cần phải sử dụng dấu ngoặc kép và bí danh (nếu bạn có tên cột bằng tên DTO).

Chỉ cần tải xuống thư viện từ trang dự án (không cần thêm lọ): fluent-hibernate.

String sql = "select firstName, lastName from Employee"; 
List<Results> employees = session.createSQLQuery(sql) 
     .setResultTransformer(new FluentHibernateResultTransformer(Results.class)) 
     .list(); 

biến này có thể được sử dụng để dự báo lồng nhau quá: How to transform a flat result set using Hibernate

0

YMMV nhưng tôi đã phát hiện ra rằng yếu tố quan trọng là bạn phải chắc chắn tới Alias ​​mọi lĩnh vực trong mệnh đề SELECT của bạn với SQL "AS "từ khóa. Tôi chưa bao giờ phải sử dụng dấu ngoặc kép xung quanh tên bí danh. Ngoài ra, trong mệnh đề SELECT của bạn sử dụng trường hợp và dấu câu của các cột thực trong cơ sở dữ liệu của bạn và trong các bí danh sử dụng trường hợp của các trường trong POJO của bạn. Điều này đã làm việc cho tôi trong Hibernate 4 và 5.

@Autowired 
private SessionFactory sessionFactory; 

... 

String sqlQuery = "SELECT firstName AS firstName," + 
     "lastName AS lastName from Employee"; 

List<Results> employeeList = sessionFactory 
     .getCurrentSession() 
     .createSQLQuery(sqlQuery) 
     .setResultTransformer(Transformers.aliasToBean(Results.class)) 
     .list(); 

Nếu bạn có nhiều bảng, bạn cũng có thể sử dụng bí danh bảng trong SQL. Ví dụ giả tạo này với một bảng bổ sung có tên "Department" sử dụng nhiều trường hợp truyền thống và gạch dưới trong các tên trường cơ sở dữ liệu với trường hợp lạc đà trong các tên trường POJO.

String sqlQuery = "SELECT e.first_name AS firstName, " + 
     "e.last_name AS lastName, d.name as departmentName" + 
     "from Employee e, Department d" + 
     "WHERE e.department_id - d.id"; 

List<Results> employeeList = sessionFactory 
     .getCurrentSession() 
     .createSQLQuery(sqlQuery) 
     .setResultTransformer(Transformers.aliasToBean(Results.class)) 
     .list(); 
0

Trong trường hợp bạn có một truy vấn nguồn gốc, tất cả các câu trả lời ở đây sử dụng phương pháp không dùng nữa trong phiên bản mới của Hibernate, vì vậy nếu bạn đang sử dụng 5.1 trở lên này là con đường để đi:

// Note this is a org.hibernate.query.NativeQuery NOT Query. 
NativeQuery query = getCurrentSession() 
       .createNativeQuery(
         "SELECT {y.*} , {x.*} from TableY y left join TableX x on x.id = y.id"); 


// This maps the results to entities. 
query.addEntity("x", TableXEntity.class); 
query.addEntity("y", TableYEntity.class); 

query.list() 
0

java.lang.ClassCastException: "CustomClass" cannot be cast to java.util.Map.

Sự cố này xuất hiện khi các cột được chỉ định trong Truy vấn SQL không khớp với các cột của lớp ánh xạ.

Nó có thể là do:

  • không khớp vỏ của tên cột hoặc

  • Các tên cột không phù hợp hoặc

  • cột tồn tại trong truy vấn nhưng còn thiếu trong lớp .

0

Viết (tồn tại kiểu này Thách thức làm việc với chế độ ngủ đông)

  1. Tuỳ chỉnh Queries
  2. Queries với Tùy chọn số tùy chỉnh
  3. Mapping Hibernate Tuỳ chỉnh kết quả truy vấn đến lớp Custom.

Tôi không nói về giao diện tùy chỉnh EntityRepository kéo dài JpaRepository trên SpringBoot mà bạn có thể viết tùy chỉnh Query với @Query -> ở đây bạn không thể viết truy vấn với params tùy chọn ví dụ nếu param là null thì không thêm nó vào chuỗi truy vấn. Và bạn có thể sử dụng Tiêu chuẩn api độ hibernate nhưng nó không được khuyến khích trong tài liệu của mình vì vấn đề hiệu suất ...

Nhưng tồn tại đơn giản và dễ bị lỗi và hiệu suất cách tốt ...

Viết lớp QueryService của riêng bạn đó là phương pháp này sẽ được string (câu trả lời cho vấn đề đầu tiên và thứ hai) sql và sẽ lập bản đồ dẫn đến lớp Custom (vấn đề thứ ba) với nó là bất cứ tổ chức @OneToMany, @ManyToOne ....

@Service 
@Transactional 
public class HibernateQueryService { 

    private final Logger log = LoggerFactory.getLogger(HibernateQueryService.class); 
    private JpaContext jpaContext; 

    public HibernateQueryService(JpaContext jpaContext) { 
     this.jpaContext = jpaContext; 
    } 

    public List executeJPANativeQuery(String sql, Class entity){ 
     log.debug("JPANativeQuery executing: "+sql); 
     EntityManager entityManager = jpaContext.getEntityManagerByManagedType(Article.class); 
     return entityManager.createNativeQuery(sql, entity).getResultList(); 
    } 

/** 
* as annotation @Query -> we can construct here hibernate dialect 
* supported query and fetch any type of data 
* with any association @OneToMany and @ManyToOne..... 
*/ 
    public List executeHibernateQuery(String sql, Class entity){ 
     log.debug("HibernateNativeQuery executing: "+sql); 
     Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class); 
     return session.createQuery(sql, entity).getResultList(); 
    } 

public <T> List<T> executeGenericHibernateQuery(String sql, Class<T> entity){ 
    log.debug("HibernateNativeQuery executing: "+sql); 
    Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class); 
    return session.createQuery(sql, entity).getResultList(); 
} 


} 

Sử dụng trường hợp - bạn có thể viết bất kỳ điều kiện loại về truy vấn params

@Transactional(readOnly = true) 
    public List<ArticleDTO> findWithHibernateWay(SearchFiltersVM filter){ 

     Long[] stores = filter.getStores(); 
     Long[] categories = filter.getCategories(); 
     Long[] brands = filter.getBrands(); 
     Long[] articles = filter.getArticles(); 
     Long[] colors = filter.getColors(); 

     String query = "select article from Article article " + 
      "left join fetch article.attributeOptions " + 
      "left join fetch article.brand " + 
      "left join fetch article.stocks stock " + 
      "left join fetch stock.color " + 
      "left join fetch stock.images "; 

boolean isFirst = true; 

     if(!isArrayEmptyOrNull(stores)){ 
      query += isFirst ? "where " : "and "; 
      query += "stock.store.id in ("+ Arrays.stream(stores).map(store -> store.toString()).collect(Collectors.joining(", "))+") "; 
      isFirst = false; 
     } 

     if(!isArrayEmptyOrNull(brands)){ 
      query += isFirst ? "where " : "and "; 
      query += "article.brand.id in ("+ Arrays.stream(brands).map(store -> store.toString()).collect(Collectors.joining(", "))+") "; 
      isFirst = false; 
     } 

     if(!isArrayEmptyOrNull(articles)){ 
      query += isFirst ? "where " : "and "; 
      query += "article.id in ("+ Arrays.stream(articles).map(store -> store.toString()).collect(Collectors.joining(", "))+") "; 
      isFirst = false; 
     } 

     if(!isArrayEmptyOrNull(colors)){ 
      query += isFirst ? "where " : "and "; 
      query += "stock.color.id in ("+ Arrays.stream(colors).map(store -> store.toString()).collect(Collectors.joining(", "))+") "; 
     } 

     List<Article> articles = hibernateQueryService.executeHibernateQuery(query, Article.class); 


     /** 
     * MapStruct [http://mapstruct.org/][1] 
     */ 
     return articles.stream().map(articleMapper::toDto).collect(Collectors.toList()); 

    } 
Các vấn đề liên quan