2016-05-24 14 views
9

Podążając za pytaniem, które napisałem wczoraj: How to populate POJO class from custom Hibernate query?mapowanie wyników kwerendy Hibernuj do niestandardowej klasy?

Czy ktoś może mi pokazać przykład, jak zakodować poniższy kod SQL w Hibernate i uzyskać poprawne wyniki?

SQL:

select firstName, lastName 
from Employee 

Co chciałbym zrobić, jeśli jest to możliwe w Hibernate, jest umieszczenie wyników w ich własnej klasy bazowej:

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

wierzę, że to możliwe w JPA (używając EntityManager), ale nie wiem, jak to zrobić w Hibernate (używając SessionFactory i Session).

Próbuję lepiej nauczyć się hibernacji, a nawet to "proste" zapytanie jest mylące, aby dowiedzieć się, w jakiej formie Hibernate zwraca wyniki i jak odwzorować wyniki na moją własną (podstawową) klasę. Więc na koniec rutyny DAO, zrobiłbym:

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

Zwracanie List z Results (moja klasa bazowa).

Odpowiedz

7

Zobacz AliasToBeanResultTransformer:

transformator wynik, który pozwala przekształcić wynik dla określonej klasy, które zostaną wypełnione przez metod dostępowych lub pól odpowiadających nazwy aliasu.

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

Kod modyfikowany:

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

Dla rodzimych zapytań SQL zobaczyć Hibernate documentation:

13.1.5. Zwracanie niezarządzanych podmiotów

Możliwe jest zastosowanie ResultTransformer do natywnych zapytań SQL, dzięki czemu może zwracać nie zarządzane podmioty.

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

Zapytanie określone: ​​

  • ciąg zapytań SQL
  • transformator wynik Powyższe zapytanie zwrócić listę CatDTO który został załadowany i wstrzyknięto wartości nazwy i BIRTHNAME do odpowiednie właściwości lub pola.
+1

śmieszne, mogę odtworzyć problem z Hibernate 5.1. Transformator 'Transformers.aliasToBean (CatDTO.class)' nie działa. Właśnie testowałem z własnym transformatorem przed :) Spróbuj dowiedzieć się, dlaczego tak jest. –

+1

To jest o wiele zabawniejsze, miałem rację z aliasami. Zobacz moją odpowiedź: http://stackoverflow.com/a/37423885/3405171 –

3

Musisz użyć konstruktora, a w hql użyć nowego.I niech wam przykład kodu pochodzący z tym pytanie: 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; 
    } 
} 

wówczas HQL

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

a java (przy użyciu 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)); 

Nie możesz użyj powyższego kodu z Hibernate 5 i Hibernate 4 (przynajmniej Hibernate 4.3.6.Final), z powodu wyjątku

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) 

Problemem jest to, że przetwarza hibernacji aliasy nazwami kolumn wersalikami - firstName się FIRSTNAME. I spróbować znaleźć getter z nazwą getFIRSTNAME() i seter setFIRSTNAME() w DTO stosując takie strategie

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

Tylko PropertyAccessStrategyMapImpl.INSTANCE garnitury, w opinii Hibernate, dobrze. Więc po tym próbuje przeprowadzić konwersję (Map)MyResults.

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

Nie wiem, jest to błąd lub funkcja.

Jak rozwiązać

Korzystanie aliasy cytatami

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

pomocą transformatora zwyczaj wynik

Innym sposobem rozwiązania problemu - przy użyciu transformatora wynik, który ignoruje nazwa przypadku metody (traktuj jako getFirstName() jako getFIRSTNAME()). Możesz napisać własną lub użyć FluentHibernateResultTransformer. Nie musisz używać cudzysłowów i cudzysłowów (jeśli masz nazwy kolumn równe nazwom DTO).

Wystarczy pobrać bibliotekę ze strony projektu (nie potrzeba dodatkowych słoików): fluent-hibernate.

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

Ten transformator może być stosowany do zagnieżdżonych projekcji TOO: How to transform a flat result set using Hibernate

0

YMMV ale odkryłem, że kluczowym czynnikiem jest należy upewnić się, aby każde pole aliasu w klauzuli SELECT z SQL „, jak "słowo kluczowe. Nigdy nie musiałem używać cytatów z nazwami aliasów. Ponadto w klauzuli SELECT użyj przypadku i interpunkcji rzeczywistych kolumn w bazie danych oraz w aliasach użyj przypadku pól w POJO. Ten pracował dla mnie w Hibernate 4 i 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(); 

Jeśli masz wiele tabel można używać aliasów tabel w SQL, jak również. Ten przykład z dodatkową tabelą o nazwie "Dział" wykorzystuje bardziej tradycyjne małe litery i podkreślenia w nazwach pól bazy danych z przypadkiem wielbłąda w nazwach pól 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

W przypadku gdy masz natywną zapytanie, wszystkie odpowiedzi tutaj użyciu przestarzałych metod nowszych wersjach Hibernate, więc jeśli używasz 5.1+ jest to sposób, aby przejść:

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

Ten problem pojawia się, gdy kolumny określone w zapytaniu SQL nie są zgodne z kolumnami klasy odwzorowania.

Może to być spowodowane:

  • niedopasowanych obudowie nazwy kolumny lub

  • nazwy kolumn nie są dopasowane lub

  • kolumny występują w zapytaniu, ale brakuje w klasie .

0

piśmie (istnieją tego typu wyzwań pracy z hibernacji)

  1. Zapytania niestandardowe
  2. tworzenia zapytań z parametrami opcjonalnymi
  3. wyniki kwerendy mapowania Hibernate niestandardowych do klasy Custom.

Nie mówię o niestandardowej EntityRepository interfejs, który rozciąga JpaRepository na SpringBoot który można napisać niestandardowy frazę @query -> tutaj nie można napisać kwerendę z opcjonalnymi params przykład jeśli parametr jest zerowy, nie dołączaj go do ciągu zapytania. I można stosować kryteria API Hibernate, ale nie zaleca się ich dokumentacji z powodu problemu z wydajnością ...

Ale istnieją proste i podatne na błędy i wydajność dobry sposób ...

Napisz własną klasę QueryService które to metody dostanie ciąg (odpowiedź dla pierwszego i drugiego problemu) SQL i map doprowadzić do niestandardowej klasy trzeciej (problem) z nim jakieś stowarzyszenie @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(); 
} 


} 

przypadków użycia - można napisać dowolny typ zapytania o stan 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()); 

    } 
Powiązane problemy