【问题标题】:spring data hibernate lazy loading春季数据休眠延迟加载
【发布时间】:2016-08-07 21:50:47
【问题描述】:

我正在学习 Spring Data/Hibernate 的东西,并面临着已知的 Hibernate 延迟加载问题。我尝试了 stackoverflow 和其他资源中描述的不同方法,但看起来它们不起作用。我在带有spring数据注释配置的coupe中使用hibernate。我的方法是:

  1. 使用 fetch = FetchType.LAZY 直接加载 @OneToMany 依赖项。正如预期的那样,我收到 LazyInitializationException 并带有关于
  2. 的消息

无法初始化代理 - 没有会话

  1. 使用事务存储库。结果与第一点相同;
  2. 使用 Spring 事务模板。结果相同;
  3. 使用 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


【解决方案1】:

您误解了 JPA 中的关系懒惰。当您将 OneToMany 关系标记为惰性关系时,实际上会变得惰性的是您的 Set 而不是 getChildren。换句话说,你需要访问 Set 的内容来触发惰性关系被获取,并且你需要一个事务来做这个。关注您的问题:

  1. 由于您的外部代码不是事务性的,因此事务本身会在存储库方法调用结束时提交。 Session 对象不再可访问,因为它已绑定到事务并且您会收到错误消息。
  2. 在您的事务模板中,您访问的是 getChildren,而不是子集的内容。这就是我在简短部分中要讨论的内容。
  3. EntityGraphs 旨在覆盖映射中定义的关系获取策略。由于您已在图表中标记了要获取的 Parent.children 字段,因此现在需要在应用此图表的每个数据库交互时急切地获取该字段。

【讨论】:

  • 感谢您的解释。现在它变得更加清晰。使我的方法 2 和 3 工作的另一个词我需要将 @Transactional 添加到我的测试方法中。
猜你喜欢
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多