it-swarm.com.de

hibernate-Abfrageergebnisse einer benutzerdefinierten Klasse zuordnen?

Im Anschluss an eine Frage, die ich gestern gepostet habe: Wie fülle ich die POJO-Klasse aus einer benutzerdefinierten Hibernate-Abfrage aus?

Kann mir jemand ein Beispiel zeigen, wie die folgende SQL in Hibernate codiert wird, und die Ergebnisse korrekt abrufen?

SQL:

select firstName, lastName
from Employee

Was ich gerne tun würde, wenn es in Hibernate möglich ist, ist, die Ergebnisse in eine eigene Basisklasse zu stecken:

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

Ich glaube, dass dies in JPA (mit EntityManager) möglich ist, aber ich habe nicht herausgefunden, wie es in Hibernate (mit SessionFactory und Session) funktioniert.

Ich versuche, Hibernate besser zu lernen, und selbst diese "einfache" Abfrage erweist sich als verwirrend, zu wissen, welche Form Hibernate die Ergebnisse zurückgibt und wie die Ergebnisse in meiner eigenen (Basis-) Klasse zugeordnet werden. Am Ende der DAO-Routine würde ich Folgendes tun:

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

rückgabe einer List von Results (meiner Basisklasse).

13
Chris
select firstName, lastName from Employee

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

Mit Hibernate 5 und Hibernate 4 (zumindest Hibernate 4.3.6.Final) können Sie den obigen Code aufgrund einer Ausnahme nicht verwenden

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)

Das Problem ist, dass Hibernate Aliase für Spaltennamen in Großbuchstaben konvertiert - firstName wird FIRSTNAME. Und es wird versucht, einen Getter mit dem Namen getFIRSTNAME() und dem Setter setFIRSTNAME() in der DTO zu finden, der solche Strategien verwendet

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

Nur PropertyAccessStrategyMapImpl.INSTANCE passt, nach Ansicht von Hibernate, gut. Danach versucht es, die Konvertierung (Map)MyResults durchzuführen.

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

Weiß nicht, es ist ein Fehler oder eine Funktion.

Wie löst man

Aliase mit Anführungszeichen verwenden

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

Verwenden eines benutzerdefinierten Ergebniswandlers

Eine andere Möglichkeit, das Problem zu lösen, ist die Verwendung eines Ergebnistransformators, bei dem die Methodennamen ignoriert werden (behandeln Sie getFirstName() als getFIRSTNAME()). Sie können selbst schreiben oder FluentHibernateResultTransformer verwenden. Sie müssen keine Anführungszeichen und Aliase verwenden (wenn Spaltennamen den DTO-Namen entsprechen). 

Laden Sie einfach die Bibliothek von der Projektseite herunter (es werden keine zusätzlichen Gläser benötigt): fluent-hibernate .

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

Dieser Transformator kann auch für verschachtelte Projektionen verwendet werden: So transformieren Sie eine flache Ergebnismenge mit Hibernate

13
v.ladynev

Siehe AliasToBeanResultTransformer :

Ergebnistransformator, der die Umwandlung eines Ergebnisses in eine vom Benutzer angegebene Klasse ermöglicht, die über Setter-Methoden oder Felder gefüllt wird, die mit den Aliasnamen übereinstimmen.

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

Ihr geänderter Code:

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

Für native SQL-Abfragen siehe Hibernate-Dokumentation :

13.1.5. Nicht verwaltete Entitäten zurückgeben

Es ist möglich, einen ResultTransformer auf native SQL-Abfragen anzuwenden, sodass nicht verwaltete Entitäten zurückgegeben werden können.

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

Diese Abfrage spezifizierte:

  • die SQL-Abfragezeichenfolge
  • ein result transformator Die obige Abfrage gibt eine Liste von CatDTO zurück, die instanziiert wurde und die Werte von NAME und BIRTHNAME in die entsprechenden Eigenschaften oder Felder eingefügt hat.
10
dur

Sie müssen einen Konstruktor verwenden und in der Hql new verwenden. Ich ließ Ihnen das Codebeispiel aus dieser Frage entnehmen: hibernate HQL createQuery () list () Typ direkt in Modell umwandeln

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

dann deine Hql

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

und Ihr Java (mit Ruhezustand)

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

YMMV, aber ich habe festgestellt, dass der Schlüsselfaktor ist, dass Sie jedes Feld in Ihrer SELECT-Klausel mit dem SQL-Schlüsselwort "AS" als Aliasnamen angeben müssen. Ich musste noch nie Anführungszeichen um die Aliasnamen verwenden. Verwenden Sie in Ihrer SELECT-Klausel außerdem die Groß- und Kleinschreibung der tatsächlichen Spalten in Ihrer Datenbank und in den Aliasnamen die Groß- und Kleinschreibung der Felder in Ihrem POJO. Das hat in Hibernate 4 und 5 für mich funktioniert.

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

Wenn Sie über mehrere Tabellen verfügen, können Sie auch Tabellenaliasnamen in SQL verwenden. Dieses Beispiel mit einer zusätzlichen Tabelle mit dem Namen "Department" verwendet traditionellere Kleinbuchstaben und Unterstriche in Datenbankfeldnamen mit Kamelbuchstaben in den POJO-Feldnamen.

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();
1
Night Owl

Schreiben (existiert diese Art von Herausforderungen, die mit dem Ruhezustand arbeiten)

  1. Benutzerdefinierte Abfragen
  2. Benutzerdefinierte Abfragen mit optionalen Parametern
  3. Zuordnen von benutzerdefinierten Abfrageergebnissen zur benutzerdefinierten Klasse.

Ich sage nicht über eine benutzerdefinierte EntityRepository-Schnittstelle, die JpaRepository auf SpringBoot erweitert, mit der Sie eine benutzerdefinierte Abfrage mit @Query schreiben können. Wenn param null ist, hängt es nicht in der Abfragezeichenfolge an. Sie können Criteria api of hibernate verwenden, dies wird jedoch in der Dokumentation aufgrund von Leistungsproblemen nicht empfohlen.

Aber es gibt einen einfachen und fehleranfälligen und leistungsfähigen Weg ...

Schreiben Sie Ihre eigene QueryService-Klasse, die als Methoden erhalten wird string (Antwort auf das erste und zweite Problem) sql und ordnet das Ergebnis auf Benutzerdefinierte Klasse (drittes Problem) mit jeder Assoziation @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();
}


}

Anwendungsfall - Sie können jede Typbedingung zu Abfrageparametern schreiben

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

    }
0
Musa

Falls Sie eine native Abfrage haben, verwenden alle Antworten hier veraltete Methoden für neuere Versionen von Hibernate. Wenn Sie also 5.1 oder höher verwenden, ist dies der Weg, den Sie verwenden müssen:

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

Unten ist ein Ergebnistransformator, der den Fall ignoriert:

package org.apec.abtc.dao.hibernate.transform;

import Java.lang.reflect.Field;
import Java.util.Arrays;
import Java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyChainedImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyFieldImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;

/**
 * IgnoreCaseAlias to BeanResult Transformer
 * 
 * @author Stephen Gray
 */
public class IgnoreCaseAliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer
{

    /** The serialVersionUID field. */
    private static final long serialVersionUID = -3779317531110592988L;

    /** The resultClass field. */
    @SuppressWarnings("rawtypes")
    private final Class resultClass;
    /** The setters field. */
    private Setter[] setters;
    /** The fields field. */
    private Field[] fields;
    private String[] aliases;

    /**
     * @param resultClass
     */
    @SuppressWarnings("rawtypes")
    public IgnoreCaseAliasToBeanResultTransformer(final Class resultClass)
    {
        if (resultClass == null)
        {
            throw new IllegalArgumentException("resultClass cannot be null");
        }
        this.resultClass = resultClass;
        this.fields = this.resultClass.getDeclaredFields();
    }

    @Override
    public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object transformTuple(final Object[] Tuple, final String[] aliases)
    {
        Object result;

        try
        {
            if (this.setters == null)
            {
                this.aliases = aliases;

                setSetters(aliases);
            }
            result = this.resultClass.newInstance();

            for (int i = 0; i < aliases.length; i++)
            {
                if (this.setters[i] != null)
                {
                    this.setters[i].set(result, Tuple[i], null);
                }
            }
        }
        catch (final InstantiationException | IllegalAccessException e)
        {
            throw new HibernateException("Could not instantiate resultclass: " + this.resultClass.getName(), e);
        }

        return result;
    }

    private void setSetters(final String[] aliases)
    {
        PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
                                                                                                         PropertyAccessStrategyBasicImpl.INSTANCE,
                                                                                                         PropertyAccessStrategyFieldImpl.INSTANCE,
                                                                                                         PropertyAccessStrategyMapImpl.INSTANCE
                                                                        );

        this.setters = new Setter[aliases.length];
        for (int i = 0; i < aliases.length; i++)
        {
            String alias = aliases[i];
            if (alias != null)
            {
                for (final Field field : this.fields)
                {
                    final String fieldName = field.getName();
                    if (fieldName.equalsIgnoreCase(alias))
                    {
                        alias = fieldName;
                        break;
                    }
                }
                setters[i] = propertyAccessStrategy.buildPropertyAccess( resultClass, alias ).getSetter();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("rawtypes")
    public List transformList(final List collection)
    {
        return collection;
    }

    @Override
    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }
        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }

        IgnoreCaseAliasToBeanResultTransformer that = ( IgnoreCaseAliasToBeanResultTransformer ) o;

        if ( ! resultClass.equals( that.resultClass ) ) {
            return false;
        }
        if ( ! Arrays.equals( aliases, that.aliases ) ) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = resultClass.hashCode();
        result = 31 * result + ( aliases != null ? Arrays.hashCode( aliases ) : 0 );
        return result;
    }
}
0
Stephen Gray

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

Dieses Problem tritt auf, wenn die in SQL Query angegebenen Spalten nicht mit den Spalten der Mapping-Klasse übereinstimmen. 

Es kann sein, dass:

  • Nicht übereinstimmendes Gehäuse des Spaltennamens oder 

  • Die Spaltennamen stimmen nicht mit oder überein 

  • spalte existiert in der Abfrage, fehlt aber in der Klasse.

0
Pankaj Shinde