Hibernate 与 SparkJava 不兼容?
Hibernate incompatible with SparkJava?
我在延迟加载模式下将 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 加载模式一样,因此它正在执行两个选择,一个用于供应商,一个用于项目。
这种行为有原因吗?
谢谢!
我猜这里是:get("/suppliers", "application/json", supplierDao::findAll);
您正在将 Supplier 对象序列化为 json。 Items
字段未标记为从序列化中排除,因此获取其值会导致会话延迟初始化(如果会话未关闭,则对项目进行冗余和第二次查询)。
如果我的猜测是正确的,请让您的序列化程序忽略 items 字段或在您的查询中获取它们
session.createQuery("FROM com.seafrigousa.model.Supplier s join fetch s.items").getResultList();
使用 gson 作为序列化程序,您有以下选项:
@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();
带有自定义注释的 ExclusionStrategy 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();
你的 class 看起来像这样
@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();
我在延迟加载模式下将 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 加载模式一样,因此它正在执行两个选择,一个用于供应商,一个用于项目。
这种行为有原因吗?
谢谢!
我猜这里是:get("/suppliers", "application/json", supplierDao::findAll);
您正在将 Supplier 对象序列化为 json。 Items
字段未标记为从序列化中排除,因此获取其值会导致会话延迟初始化(如果会话未关闭,则对项目进行冗余和第二次查询)。
如果我的猜测是正确的,请让您的序列化程序忽略 items 字段或在您的查询中获取它们
session.createQuery("FROM com.seafrigousa.model.Supplier s join fetch s.items").getResultList();
使用 gson 作为序列化程序,您有以下选项:
@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();
带有自定义注释的 ExclusionStrategy 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();
你的 class 看起来像这样
@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();