JPA单向OneToMany产生太多结果

时间:2018-03-22 12:52:50

标签: java hibernate spring-boot spring-data-jpa one-to-many

我在使用没有JoinTables的单向OneToMany JPA映射时遇到了困难。

包括嵌套的完整实体看起来基本上是这样的:

Parent
  └ ChildLvl1
     └ ChildLvl2
        └ Attribute

问题是我可以很好地插入一个复杂的实体,当我用repository.findAll()检索它时它工作正常,结果对象是预期的,但当我使用api方法repository.getOne(Long)时,它返回封闭的许多实体的次数太多了。 太多的次数取决于我插入的属性数量。

因此,如果我使用带有一个属性的childLvl2插入父级,则两种方法都会产生相同的结果。 但是让我说我​​插入一个带有两个属性的childLvl2,repository.findAll()返回以下结果:

parent
  └ childLvl1
     └ childLvl2
        ├ attribute_1
        └ attribute_1

repository.getOne(Long)返回以下结果:

parent  (id=1)
  ├ childLvl1   (id=1)
  │  ├ childLvl2   (id=1)
  │  │  ├ attribute_1   (id=1)
  │  │  └ attribute_2   (id=2)
  │  └ childLvl2   (id=1)
  │     ├ attribute_1   (id=1)
  │     └ attribute_2   (id=2)
  └ childLvl1   (id=1)
     ├ childLvl2   (id=1)
     │  ├ attribute_1   (id=1)
     │  └ attribute_2   (id=2)
     └ childLvl2   (id=1)
        ├ attribute_1   (id=1)
        └ attribute_2   (id=2)

(id = 1)这里表示数据库表中实体的内部ID。事实上,如果我直接在数据库中查看它包含我期望的数据,例如for ChildLvl1:

SELECT * FROM CHILD_LVL1;

ID    | NAME  | PARENT_ID  
------|-------|-----------
    1 |  TEST |         1

这表明数据库内部的所有内容似乎都符合预期,但hibernate对repository.getOne(Long)的映射似乎无法正常工作。

我也尝试使用@OneToMany(mappedBy="xxx")代替@JoinColumn,但我得到了相同的结果: - /,所以我猜错误是在其他地方。

查看hibernate执行的SQL,我发现这两种方法使用不同的查询,findAll()执行多个查询然后构建实体,而getOne只执行一个选择 join < / em>,但我真的希望得到同样的结果......

我的实体设置如下:

@Transactional
@Entity
@XmlRootElement
public class Parent {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id;

    String name;
    @OneToMany(cascade= CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="PARENT_ID", referencedColumnName="ID")
    List<ChildLvl1> 

...
}
@Transactional
@Entity
@XmlType
public class ChildLvl1 {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id;

    String name;

    @OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="C_LVL1_ID", referencedColumnName="ID")
    List<ChildLvl2> lvl2children;

...
}
@Transactional
@Entity
@XmlType
public class ChildLvl2 {

    @Id
    @Column(name="C_LVL2_ID")
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id;

    String name;

    @OneToMany(cascade= CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="C_LVL2_ID")
    List<Attribute> attributes;

...
}
@Transactional
@Entity
@XmlType
public class Attribute {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id;

    String key;
    String value;

...
}

spring-data jpa repo:

public interface ParentRepository extends JpaRepository<Parent, Long>, QueryDslPredicateExecutor<Parent> {

    public List<Parent> findByName(String name);

}

我的测试用例中的用法示例:

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestEntityCount {

    @Autowired
    ParentRepository repository;

    @PostConstruct
    public void postConstruct() {
        if (!initialized) {
            logger.debug("Setting up repository");

            Parent parent = new Parent();
            parent.setName("TEST_PARENT");

            List<ChildLvl1> lvl1_children = new ArrayList<>();
            ChildLvl1 lvl1_child = new ChildLvl1();
            lvl1_child.setName("TEST_CHILD1");

            List<ChildLvl2> lvl2_children = new ArrayList<>();
            ChildLvl2 lvl2_child = new ChildLvl2();
            lvl2_child.setName("TEST_CHILD2");

            List<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("KEY1", "VALUE1"));
            attributes.add(new Attribute("KEY2", "VALUE2"));

            lvl1_children.add(lvl1_child);
            parent.setChildren1(lvl1_children);
            lvl2_children.add(lvl2_child);
            lvl1_child.setLvl2children(lvl2_children);
            lvl2_child.setAttributes(attributes);

            repository.save(parent);

            initialized = true;
        }
    }

    @Test
    public void testFindAll() {  // -> OK
        Parent parent = repository.findAll().get(0);
        logger.debug("Parent retrieved : {}", parent);
        assertEquals(1, parent.getChildren1().size());
        assertEquals(2, parent.getChildren1().get(0).getLvl2children().get(0).getAttributes().size());
    }

    @Test
    @Transactional
    public void testGetById() {  // -> java.lang.AssertionError: expected:<1> but was:<2>
        Parent parent = repository.getOne(1L);
        logger.debug("Parent retrieved : {}", parent);
        assertEquals(1, parent.getChildren1().size());
        assertEquals(2, parent.getChildren1().get(0).getLvl2children().get(0).getAttributes().size());
    }
...

我的项目使用

  • Spring boot 1.5.10.RELEASE
  • Spring data jpa 1.11.10.RELEASE
  • hibernate 5.0.12.FINAL

我已经设置了example project @Github,包括一个以可重现的方式演示问题的测试用例。

0 个答案:

没有答案