【发布时间】:2016-08-07 21:50:47
【问题描述】:
我正在学习 Spring Data/Hibernate 的东西,并面临着已知的 Hibernate 延迟加载问题。我尝试了 stackoverflow 和其他资源中描述的不同方法,但看起来它们不起作用。我在带有spring数据注释配置的coupe中使用hibernate。我的方法是:
- 使用 fetch = FetchType.LAZY 直接加载 @OneToMany 依赖项。正如预期的那样,我收到 LazyInitializationException 并带有关于 的消息
无法初始化代理 - 没有会话
- 使用事务存储库。结果与第一点相同;
- 使用 Spring 事务模板。结果相同;
- 使用 JPA 2.1 的 @NamedEntityGraph。这里的情况更有趣。我的测试通过了,但是在 SQL 调试中我可以看到数据没有延迟加载,而是第一次查询存储库。换句话说,子表在我第一次查询存储库时加入了父表。
我将我的测试项目推送到了 github,所以可以在那里查看它 https://github.com/megamaxskx/hibernate_lazy_fetch
更新
Spring事务模板方法: 存储库:
@Repository
public interface PlainParentRepository extends CrudRepository<PlainParent, Long> {
}
配置:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = REPOSITORY_LOCATION)
public class DBConfig {
public static final String REPOSITORY_LOCATION = "com.lazyloadingtest";
private static final String ENTITIES_LOCATION = "com.lazyloadingtest";
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.H2);
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lemfb = new LocalContainerEntityManagerFactoryBean();
lemfb.setDataSource(dataSource());
lemfb.setJpaVendorAdapter(jpaVendorAdapter());
lemfb.setPackagesToScan(ENTITIES_LOCATION);
lemfb.setJpaProperties(hibernateProperties());
return lemfb;
}
protected Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.format_sql", true);
return properties;
}
}
儿童班:
@Entity
public class EntityGraphChild implements Serializable {
public static long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
private PlainParent parent;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PlainParent getParent() {
return parent;
}
public void setParent(PlainParent parent) {
this.parent = parent;
}
}
父类:
@Entity
public class PlainParent implements Serializable {
public static long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private String name;
@OneToMany(targetEntity = PlainChild.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<PlainChild> children;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<PlainChild> getChildren() {
return children;
}
public void setChildren(Set<PlainChild> children) {
this.children = children;
}
}
Spring事务模板测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
DBConfig.class,
})
public class SpringTransactionTemplateTest {
@Autowired
private PlainParentRepository repository;
@Autowired
private JpaTransactionManager transactionManager;
@Test
public void testRepository() {
PlainChild child1 = new PlainChild();
child1.setName("first child");
PlainChild child2 = new PlainChild();
child2.setName("second child");
PlainParent parent = new PlainParent();
parent.setId(1l);
HashSet<PlainChild> children = new HashSet<PlainChild>(Arrays.asList(child1, child2));
parent.setChildren(children);
repository.save(parent);
TransactionTemplate txTemplate = new TransactionTemplate();
txTemplate.setTransactionManager(transactionManager);
Set<PlainChild> fromDB = txTemplate.execute(new TransactionCallback<Set<PlainChild>>() {
public Set<PlainChild> doInTransaction(TransactionStatus transactionStatus) {
PlainParent fromDB = repository.findOne(1L);
return fromDB.getChildren();
}
});
assertEquals(2, fromDB.size());
}
}
NamedEntityGraph 方法:
孩子:
@Entity
public class EntityGraphChild implements Serializable {
public static long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
private EntityGraphParent parent;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public EntityGraphParent getParent() {
return parent;
}
public void setParent(EntityGraphParent parent) {
this.parent = parent;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EntityGraphChild child = (EntityGraphChild) o;
if (id != child.id) return false;
return name != null ? name.equals(child.name) : child.name == null;
}
}
家长:
@Entity
@NamedEntityGraph(
name = "graph.Parent.children",
attributeNodes = @NamedAttributeNode(value = "children")
)
public class EntityGraphParent implements Serializable {
public static long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private String name;
@OneToMany(targetEntity = EntityGraphChild.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<EntityGraphChild> children;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<EntityGraphChild> getChildren() {
return children;
}
public void setChildren(Set<EntityGraphChild> children) {
this.children = children;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EntityGraphParent parent = (EntityGraphParent) o;
if (id != parent.id) return false;
if (name != null ? !name.equals(parent.name) : parent.name != null) return false;
return children != null ? children.equals(parent.children) : parent.children == null;
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (children != null ? children.hashCode() : 0);
return result;
}
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
DBConfig.class,
})
public class EntityGraphParentRepositoryTest {
@Autowired
private EntityGraphParentRepository repository;
@Test
public void testRepository() {
EntityGraphChild child1 = new EntityGraphChild();
child1.setName("first child");
EntityGraphChild child2 = new EntityGraphChild();
child2.setName("second child");
EntityGraphParent parent = new EntityGraphParent();
parent.setId(1l);
parent.setName("ParentGraph");
HashSet<EntityGraphChild> children = new HashSet<EntityGraphChild>(Arrays.asList(child1, child2));
parent.setChildren(children);
repository.save(parent);
System.out.println("--- Before querying repo");
EntityGraphParent fromDB = repository.findByName("ParentGraph");
System.out.println("--- After querying repo");
assertEquals(2, fromDB.getChildren().size());
System.out.println("--- Test finished");
}
}
存储库:
@Repository
public interface EntityGraphParentRepository extends CrudRepository<EntityGraphParent, Long> {
@EntityGraph(value = "graph.Parent.children", type = EntityGraph.EntityGraphType.LOAD)
public EntityGraphParent findByName(String name);
}
【问题讨论】:
-
在这里发布最少的相关代码,不要只是链接它们
-
使用hibernate最好的东西:sqlqueries
-
首先,请发布完整的堆栈跟踪
-
如果您尝试访问延迟加载的属性而不初始化它们,我们通常会看到此错误。所以最好的方法是 hibernate.instance(yourentity.getwhateveryouretrttingtoretreieve) 这个代码将初始化你的惰性标记属性
标签: java spring hibernate jpa spring-data