休眠与SparkJava不兼容吗?

时间:2019-03-29 23:29:35

标签: java hibernate lazy-initialization spark-java

在懒加载模式下将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加载模式一样,因此它正在执行两个选择,一个用于“供应商”,一个用于“项目”。

是否有这种行为的原因?

谢谢!

1 个答案:

答案 0 :(得分:2)

我想在这里:get("/suppliers", "application/json", supplierDao::findAll); 您正在将Supplier对象序列化为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中将Supplieritems进行序列化,则可以为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();