简体   繁体   English

坚持Map<entity, integer> 使用@ElementCollection (JPA)</entity,>

[英]Persisting Map<Entity, Integer> with @ElementCollection (JPA)

I'm trying to learn more about java persistance api.我正在尝试了解有关 java 持久性 api 的更多信息。

In my test maven project i'm trying to persist the following class:在我的测试 maven 项目中,我试图坚持以下 class:

@Entity
@Table(name = "component")
public class DishComponent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private int id;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "component_supplement", joinColumns = 
    @JoinColumn(name="component_id"))
    @Column(name = "quantity")
    @MapKeyJoinColumn(name = "supplement_id", referencedColumnName = "id")
    private Map<Supplement, Integer> supplements;  
}

into H2 database.进入H2数据库。

Supplement class:补充class:

@Entity
public class Supplement {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private int id;
}

schema.sql:架构.sql:

DROP TABLE IF EXISTS component;
create table component  (
    id INT PRIMARY KEY auto_increment,
    name VARCHAR(255) UNIQUE
);

DROP TABLE IF EXISTS supplement;
create table supplement (
    id INT PRIMARY KEY auto_increment,
    name VARCHAR(255) UNIQUE
);

drop TABLE IF EXISTS component_supplement;
create TABLE component_supplement (
    id INT PRIMARY KEY auto_increment,
    supplement_id INT REFERENCES supplement(id),
    component_id INT REFERENCES component(id),
    quantity INT NOT NULL
);

When I try to persist DishComponent instance into db I get the following error:当我尝试将 DishComponent 实例持久化到 db 中时,出现以下错误:

java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing

Test:测试:

@DataJpaTest
@ContextConfiguration(classes = DaoConfig.class)
@TestPropertySource(locations = "/test-application.properties")
@Sql(scripts = "/schema.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
class DishComponentDaoImplTest {

    @Autowired
    private TestEntityManager em;

    @Autowired
    private DishComponentDao repo;

    @Test
    void whenGetById_returnDishComponent() {
        String suppName = "supp1";
        Supplement supp1 = new Supplement(suppName);
        String name = "test";
        DishComponent dishComponent = DishComponent.getBuilder(name).addSupplement(supp1, 10).build();

        DishComponent persist = em.persist(dishComponent);
        em.flush();

        DishComponent component = repo.getById(persist.getId());
        assertThat(component.getName()).isEqualTo(name);
        Map<Supplement, Integer> returnedSup = component.getSupplements();
        Integer quantity = returnedSup.get(supp1);
        assertThat(quantity).isEqualTo(10);
    }
}

What am I doing wrong?我究竟做错了什么?

For this project I'm using spring boot 2.2.1.RELEASE with spring-boot-starter-data-jpa and com.h2database:h2:1.4.198对于这个项目,我使用 spring boot 2.2.1.RELEASE 和 spring-boot-starter-data-jpa 和 com.h2database:h2:1.4.198

em.persist(dishComponent) does not persist the Supplement object, that needs to be saved first with a separate persist call. em.persist(dishComponent)不会持久化补充 object,需要先通过单独的持久调用来保存。

Your code is really close to this example so check it out: http://www.java2s.com/Tutorials/Java/JPA/0320__JPA_ElementCollection_MapKeyEntity.htm您的代码非常接近此示例,因此请查看: http://www.java2s.com/Tutorials/Java/JPA/0320__JPA_ElementCollection_MapKeyEntity.htm

You mappings are valid but unusual ( @ElementCollection is normally used with a collection of embeddables and not entities however it can be used as you have here).您的映射是有效但不寻常的( @ElementCollection通常与可嵌入的集合而不是实体一起使用,但它可以像您在这里一样使用)。

From the tables you have a many-to-many between component and supplement.从表格中,您可以在组件和补充之间找到多对多。 However as you want to store an additional property on the relationship - quantity - you will need a 3rd entity for the join.但是,由于您想在关系上存储一个附加属性 - quantity - 您将需要第三个实体来进行连接。

I don not see that mapping as you have it can fit your table structure.我没有看到您拥有的映射可以适合您的表结构。

The following is a more 'standard' way of mapping such a relationship and allows for greater flexibility and fits your table structure.以下是映射此类关系的更“标准”方式,并允许更大的灵活性并适合您的表结构。

Component Entity:组件实体:

@Entity
public class Component {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private Long id;

    @OneToMany(mappedBy = "component")
    private Set<ComponentSupplement> suplements;
}

Supplement Entity:补充实体:

@Entity
public class Supplement {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private Long id;

    @OneToMany(mappedBy = "supplement")
    private Set<ComponentSupplement> suplements;
}

'Join' Entity: “加入”实体:

@Entity
@Table(name = "component_supplement")
public class ComponentSupplement {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    private int id;

    @ManyToOne(cascase = Cascadetype.ALL)
    @JoinColumn(name = "component_id")
    private Component component;

    @ManyToOne(cascase = Cascadetype.ALL)
    @JoinColumn(name = "supplement_id")
    private Supplement supplement;

    private int quantity;
}

Construct and Save:构建和保存:

Component component = new Component();
Supplement supplement = new Supplement();
ComponentSupplement cs = new ComponentSupplement();
cs.setComponent(component);
cs.setSupplement(supplement);
em.persist(cs);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM