java - تعيين نتائج الاستعلام السبات إلى فئة مخصصة؟




spring hibernate (6)

YMMV لكنني وجدت أن العامل الرئيسي هو أنه يجب عليك التأكد من الاسم المستعار لكل حقل في جملة SELECT باستخدام الكلمة الأساسية "AS" في SQL. لم أكن مضطرًا أبدًا لاستخدام علامات الاقتباس حول أسماء الاسم المستعار. أيضًا ، في جملة SELECT ، استخدم الحالة وعلامات الترقيم للأعمدة الفعلية في قاعدة البيانات الخاصة بك وفي الأسماء المستعارة استخدم حالة الحقول في POJO. وقد عملت هذه بالنسبة لي في السبات 4 و 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();

إذا كان لديك جداول متعددة ، يمكنك استخدام الأسماء المستعارة للجدول في SQL أيضًا. يستخدم هذا المثال المفترض بجدول إضافي يسمى "القسم" أحرفًا أقل تقليدية وتسطير أسفل السطر في أسماء حقول قاعدة البيانات مع حالة الجمل في أسماء حقول 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();

متابعة لسؤال نشرته بالأمس: كيف يتم تعبئة فئة POJO من استعلام السبات المخصص؟

هل يمكن لأي شخص أن يوضح لي مثالًا على كيفية ترميز SQL التالية في السبات ، والحصول على النتائج بشكل صحيح؟

SQL:

select firstName, lastName
from Employee

ما أود القيام به ، إذا كان ذلك ممكنًا في Hibernate ، هو وضع النتائج في فئتها الأساسية:

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

أعتقد أن ذلك ممكن في JPA (باستخدام EntityManager ) ، لكنني لم EntityManager إلى معرفة كيفية القيام بذلك في الإسبات (باستخدام SessionFactory و Session ).

أحاول تعلم Hibernate بشكل أفضل ، وحتى هذا الاستعلام "البسيط" يثبت أنه من المربك معرفة الشكل الذي يُرجع Hibernate النتائج إليه ، وكيفية تعيين النتائج في صفي (الأساسي) الخاص بي. حتى في نهاية روتين DAO ، كنت أفعل:

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

إرجاع List Results (صفي الأساسي).


الكتابة (توجد هذا النوع من التحديات في العمل مع السبات)

  1. استعلامات مخصصة
  2. استعلامات مخصصة مع معلمات اختيارية
  3. تعيين نتائج الاستعلام السبات مخصص إلى فئة مخصصة.

أنا لا أقول عن واجهة EntityRepository المخصصة التي تمد JpaRepository على SpringBoot والتي يمكنك كتابة استعلام مخصص باستخدامQuery -> هنا لا يمكنك كتابة استعلام باستخدام params اختياريًا ، على سبيل المثال ، إذا كانت param is null لا تضيفها في سلسلة الاستعلام. ويمكنك استخدام معايير api of hibernate ولكنها غير موصى بها في وثائقها بسبب مشكلة الأداء ...

ولكن الوجود بسيط والخطأ والأداء بطريقة جيدة ...

اكتب فئة QueryService الخاصة بك والتي هي طرق ستحصل على سلسلة (الإجابة عن المشكلتين الأولى والثانية) sql وسيتم تعيين نتيجة للفئة المخصصة (المشكلة الثالثة) مع أي ارتباط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();
}


}

استخدام الحالة - يمكنك كتابة أي شرط نوع حول المعلمات الاستعلام

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

    }

تحتاج إلى استخدام المنشئ وفي hql استخدام جديد. اسمح لك مثال التعليمات البرمجية المأخوذ من هذا السؤال: الإسبات HQL createQuery () list () type cast to model مباشرة

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

ثم hql الخاص بك

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

وجافا الخاص بك (باستخدام السبات)

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

في حال كان لديك استفسار أصلي ، تستخدم جميع الإجابات هنا طرقًا مهملة للإصدارات الأحدث من Hibernate ، لذلك إذا كنت تستخدم الإصدار 5.1 أو أحدث ، فهذه هي الطريقة التي يجب اتباعها:

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

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

تظهر هذه المشكلة عندما لا تتطابق الأعمدة المحددة في استعلام SQL مع أعمدة فئة التعيين.

قد يكون بسبب:

  • غلاف غير مطابق لاسم العمود أو

  • أسماء الأعمدة ليست مطابقة أو

  • العمود موجود في الاستعلام ولكن مفقود في الفصل الدراسي.


select firstName, lastName from Employee

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

لا يمكنك استخدام الرمز أعلاه مع Hibernate 5 و Hibernate 4 (على الأقل Hibernate 4.3.6.Final) ، بسبب استثناء

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)

المشكلة هي أن الإسبات يحول الأسماء المستعارة لأسماء الأعمدة إلى الحالة العليا - يصبح firstName FIRSTNAME . ويحاول العثور على getter باسم getFIRSTNAME() ، و setter setFIRSTNAME() في DTO باستخدام مثل هذه الاستراتيجيات

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

فقط PropertyAccessStrategyMapImpl.INSTANCE يناسب ، في رأي السبات ، حسنا. لذلك بعد ذلك يحاول القيام بتحويل (Map)MyResults .

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

لا أعرف ، إنها خطأ أو ميزة.

كيفية حل

باستخدام الأسماء المستعارة مع علامات الاقتباس

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

باستخدام محول نتيجة مخصصة

هناك طريقة أخرى لحل المشكلة - استخدام محول النتائج الذي يتجاهل حالة أسماء الطرق (تعامل مع getFirstName() كـ getFIRSTNAME() ). يمكنك كتابة الخاصة بك أو استخدام FluentHibernateResultTransformer . لن تحتاج إلى استخدام علامات الاقتباس والأسماء المستعارة (إذا كان لديك أسماء أعمدة تساوي أسماء DTO).

ما عليك سوى تنزيل المكتبة من صفحة المشروع (لا تحتاج إلى جرار إضافية): fluent-hibernate .

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

يمكن استخدام هذا المحول للإسقاطات المتداخلة أيضًا: كيفية تحويل مجموعة نتائج مسطحة باستخدام السبات







hibernate