【问题标题】:JPA/Hibernate: Multiple unnecessary entires in a tableJPA/Hibernate:表中有多个不必要的条目
【发布时间】:2015-11-07 10:21:02
【问题描述】:

我是新来的,我也是 JPA 的新手!我开发了一个带有以下实体的小型 JPA 应用程序:

客户(姓名、名字、身份证)有一个地址(邮政编码、城市、身份证)。这些实体之间的关系是多对一(客户的视角)和一对多(地址的视角)。

代码(部分)如下:(没有getter/setter)

@Entity
public class AddressEntity implements Serializable{
private static final long serialVersionUID = 1L;

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

private String city;    
private int zipCode;


@Column
@ElementCollection(targetClass=CustomerEntity.class)
private List<CustomerEntity> customers;


public AddressEntity() {}


@OneToMany(cascade=CascadeType.ALL,
        fetch=FetchType.EAGER,
        mappedBy="addressentity")
public List<CustomerEntity> getCustomers() {
    return customers;
}



@Entity
public class CustomerEntity implements Serializable {

    private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int primaryKey;

private String preName, surName;

@ManyToOne(targetEntity=AddressEntity.class, cascade=CascadeType.ALL)
@JoinColumn(name="Address_ID")
private AddressEntity address;

public CustomerEntity() {}

}

现在我想添加两个人: 1) 巴特辛普森 1234 斯普林菲尔德 2) 荷马辛普森 1234 斯普林菲尔德

问题是地址表中Springfield出现了两次。但这不是规范化数据库的意义!我怎么能意识到只有在之前不存在的情况下才添加 AddressEntity!

感谢您的回答, 埃尼亚克

【问题讨论】:

  • 可能有更好的方法,但尝试先将地址持久化,然后将其添加到 2 个用户并持久化它们。为此,您必须将CustomerEntity 中的CascadeType.ALL 删除或更改为其他内容(CascadeType.REFRESH 可能会这样做)。无论如何使用 ALL 似乎有点奇怪,主要是因为在您当前的代码中,删除客户将摆脱可能分配给另一个用户的地址。
  • 感谢您的建议!但是你描述的方式和之前的效果是一样的
  • 其中一些问题取决于您正在处理的实体的附加性,您能否在保留这些实体的位置添加代码摘录?

标签: java hibernate jpa mapping database-normalization


【解决方案1】:

保留新地址

可能有多个AddressEntities 具有相同的City 和Zipcode。如果您要求给定的CustomerEntity 与相同的address 相关,那么您必须使用该特定 实体。因此,请使用与 Bart for Homer 相同的 AddressEntity

CustomerEntity bart = new CustomerEntity();
//Set Barts’s fields.
CustomerEntity homer = new CustomerEntity();
//Set Homers’s fields.


AddressEntity simpsonsPlace = new AddressEntity();
//set 1234 and Springfield.

bart.setAdress(simpsonsPlace);
homer.setAdress(simpsonsPlace);

List<CustomerEntity> simpsons = new ArrayList<CustomerEntity>();
simpsons.add(bart);
simpsons.add(homer);
simpsonsPlace.setCustomers(simpsons);

yourEntityManager.persist(bart);
yourEntityManager.persist(homer);   

与现有地址关联。

如果您在 bart 之后的某个时间创建本垒打,那么您需要将 bart 关联到现有的 address。您可能会从客户端传入address。这将填充 id 字段并且将是一个 分离的 实体。您可以将此实体与em.merge() 操作一起使用;

CustomerEntity homer = new CustomerEntity();
homer.setAdress(detachedAddress);
yourEntityManager.merge(homer);

例如,更好的方法可能是形成 Zip/HouseNumber 的复合 PK。这确保了数据库中已有的任何带有 Zip/HouseNumber 组合的AddressEntity 都将被视为分离对象,当然只会在数据库中出现一次。

嵌入式

@ElementCollection 和 @OneToMany 都针对您的 CustomerEntityEntity。

根据 JPA 规范 11.1.14;

ElementCollection 注解定义了一个实例的集合 基本类型或可嵌入类。

因此,使用@ElementCollection 定位实体是不正确的。

您需要两个实体之间的关系,因此,在您的AddressEntity 中,您只需要使用@OneToMany 并删除@ElementCollection。您还需要将(使用mappedBy)映射到目标类的字段,而不是目标类本身;

@Entity
public class AddressEntity implements Serializable{
private static final long serialVersionUID = 1L;

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

private String city;    
private int zipCode;


@OneToMany(cascade=CascadeType.ALL,
        fetch=FetchType.EAGER,
        mappedBy="address") //Not addressentity
private List<CustomerEntity> customers;


public AddressEntity() {}

public List<CustomerEntity> getCustomers() {
    return customers;
}

或者,您可以将 CustomerEntity 定义为 @Embeddable 而不是 @entity。通过这种方式,您可以使用@ElementCollection 从AddressEntity 定位CustomerEntity,但CustomerEntity 本身不是一个实体,不能独立处理,因为它依赖于AddressEntity

【讨论】:

  • 感谢您的回答。删除 @ElementCollection 并选择注释方式并不能解决问题
  • @Eniac - 查看更新,您的地址实体是不同的实体,将被保留两次,您需要确保将客户与相同的 addressEntity 相关联,而这实际上是同一个地址。跨度>
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 2016-07-07
  • 2016-12-12
  • 2019-10-04
  • 2011-01-25
  • 2016-03-14
  • 2012-01-10
  • 2017-07-13
相关资源
最近更新 更多