【问题标题】:Hibernate incompatible with SparkJava?Hibernate 与 SparkJava 不兼容?
【发布时间】:2019-03-29 23:29:34
【问题描述】:

我在延迟加载模式下使用 Hibernate 和 SparkJava 时出错。

在没有 SparkJava 的情况下它可以正常工作,但是在使用 SparkJava 时,它会尝试强制为 OneToMany 关系进行预加载。


- 型号

@Entity
@Table(name = "KU_SUPPLIER")
public class Supplier {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @NotEmpty(message = "Please provide a name")
    private String name;

    @OneToMany(mappedBy = "supplier")
    private List<Item> items;  // Should be lazy-loaded

    // Constructor / Getters / Setters
}


- DAO

public class SupplierDao implements Dao<Supplier> {

    private final SessionFactory sessionFactory;

    public SupplierDao(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Supplier> findAll() {
        try (Session session = sessionFactory.openSession()) {
            return session.createQuery("FROM com.seafrigousa.model.Supplier").getResultList();
        }
    }
}


- 主要

// Working perfectly and lazy-load Items as desired    
supplierDao.findAll();

// The method will be called when a web browser goes to "localhost/suppliers"
// It throws org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: model.Supplier.items, could not initialize proxy - no Session
get("/suppliers", "application/json", supplierDao::findAll);


我通过不关闭 DAO 的会话进行检查,发现 Hibernate 正在执行查询,就好像它处于 EAGER 加载模式一样,因此它正在执行两个选择,一个用于供应商,一个用于项目。

这种行为有原因吗?

谢谢!

【问题讨论】:

    标签: java hibernate lazy-initialization spark-java


    【解决方案1】:

    我猜这里:get("/suppliers", "application/json", supplierDao::findAll); 您正在将供应商对象序列化为 json。 Items 字段未标记为从序列化中排除,因此获取其值会导致会话延迟初始化(如果会话未关闭,则对项目进行冗余和第二次查询)。

    如果我的猜测是正确的,让您的序列化程序忽略项目字段或在您的查询中获取它们

    session.createQuery("FROM com.seafrigousa.model.Supplier s join fetch s.items").getResultList();
    

    使用 gson 作为序列化程序,您有以下选项:

    1. @Expose 对您想要序列化的字段进行注释。

      @Entity
      @Table(name = "KU_SUPPLIER")
      public class Supplier {
      
          @Expose
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private int id;
      
          @Expose
          @NotEmpty(message = "Please provide a name")
          private String name;
      
          @OneToMany(mappedBy = "supplier")
          private List<Item> items;  // Should be lazy-loaded
      
          // Constructor / Getters / Setters
      }
      

      通过以下 gson 启动

      Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
      
    2. 带有自定义注释 f.e. 的排除策略

      public class IgnoreFieldExclusionStrategy implements ExclusionStrategy {
      
          @Override
          public boolean shouldSkipField(FieldAttributes fieldAttributes) {
              return fieldAttributes.getAnnotation(GsonIgnore.class) != null;
          }
      
          @Override
          public boolean shouldSkipClass(Class<?> aClass) {
              return false;
          }
      }
      

      带有自定义注解@GsonIgnore

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.FIELD)
      public @interface GsonIgnore {}
      

      和gson启动

      Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new IgnoreFieldExclusionStrategy()).create();
      

      你的班级应该是这样的

      @Entity
      @Table(name = "KU_SUPPLIER")
      public class Supplier {
      
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private int id;
      
          @NotEmpty(message = "Please provide a name")
          private String name;
      
          @GsonIgnore
          @OneToMany(mappedBy = "supplier")
          private List<Item> items;  // Should be lazy-loaded
      
          // Constructor / Getters / Setters
      }
      

    如果您需要在不同的 api 中使用 items 序列化 Supplier,您可以为 Supplier 创建 DTO 对象并从如下结果映射它:

    package com.seafrigousa.dto
    
    public class SupplierDTO {
    
        private int id;
        private String name;
    
        public SupplierDTO(int id, String name) {
            this.id = id;
            this.name = name;
       }
    
        // Getters / Setters
    }
    

    和查询:

    session.createQuery("select new com.seafrigousa.dto.SupplierDTO(s.id, s.name) FROM com.seafrigousa.model.Supplier s").getResultList();
    

    【讨论】:

    • 我猜你也是对的!您能否提供一个使用 Gson 作为序列化程序(由 SparkJava 使用)的被忽略字段的示例?唯一缺少的是接受它的赏金:)
    • 完美,感谢您的帮助!这是奖励! :)
    猜你喜欢
    • 2016-10-03
    • 2016-10-12
    • 2017-09-08
    • 2020-02-23
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 2016-07-26
    相关资源
    最近更新 更多