繁体   English   中英

了解JPA中的一对多关系

[英]Understand OneToMany Relationship in JPA

我在一对多关系中有两个实体:

父实体:

@Entity
@Table(name = "PIANOTAGLIE")
public class PianoTaglia {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String pianoTaglia;

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "pianoTaglia", cascade = CascadeType.ALL)
    private List<Taglia> taglie;

    public PianoTaglia() {
    }

    [...] Getter/Setter [...]

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PianoTaglia other = (PianoTaglia) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }
}

和子实体:

@Entity
@Table(name = "TAGLIE")
public class Taglia {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 10, unique = true)
    private String taglia;

    @ManyToOne
    private PianoTaglia pianoTaglia;

    public Taglia() {
    }

    [...] Getter/Setter [...]

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Taglia other = (Taglia) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }
}

为了管理我的实体,我使用以下通用Dao:

public abstract class JpaDAO<E> {

    protected Class<E> entityClass;

    @PersistenceContext(unitName = "PrudiPU")
    protected EntityManager em;

    @SuppressWarnings("unchecked")
    public JpaDAO() {
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[0];
    }

    public List<E> findAll() {
        TypedQuery<E> q = em.createQuery("SELECT h FROM " + entityClass.getName() + " h", entityClass);
        return q.getResultList();
    }

    public void persist(E entity) {
        em.persist(entity);
    }

    public E getReference(Long id) {
        return em.getReference(entityClass, id);
    }      
}

专门针对每个班级(这是PianoTagliaDao,但TagliaDao相同)

@Repository
public class PianoTaglieDao extends JpaDAO<PianoTaglia> {

}

创建PianoTaglia时,我会使用生成的ID保留对对象的引用...这样我就可以浏览应用程序,并随时可以创建Taglia。 当我创建Taglia时,我以这种方式使用对PianoTaglia的引用,该引用是先前创建的:

PianoTaglia pt = getPreviuslyCreatedPianoTaglia(); //this is an example
Taglia tg = new Taglia();
tg.setTaglia("XXL");
tg.setPianoTaglia(pt);
pt.getTaglie().add(tg);
taglieDao.persist(tg);
taglieDao.flush(); //i need to flush for keep generated ID
[...]

如果我将表检查到DB都可以! 所有的桌子都装满了! 但是,如果我尝试获取所有PianoTaglia,则taglie集合始终为空:

List<PianoTaglia> pianoTagle = pianoTagliaDao.findAll();
for(PianoTaglia pt : pianoTaglie) {
    assert pt.getTaglie().isEmpty();
}

经过测试后,我找到了解决方案:创建taglia时,我必须保留PianoTaglie的新引用:

PianoTaglia old = getPreviuslyCreatedPianoTaglia();
PianoTaglia pt = pianoTaglieDao.getReference(old.getId()); //getReference call the namesake method in the EntityManager
Taglia tg = new Taglia();
tg.setTaglia("XXL");
tg.setPianoTaglia(pt);
pt.getTaglie().add(tg);
taglieDao.persist(tg);
taglieDao.flush(); //i need to flush for keep generated ID
[...]

这样,当我保留PianoTaglia对象时,就很好地填充了taglie集合。

我的问题是:为什么JPA会有这种行为?

看起来您正在存储先前创建的PianoTaglia,并在上下文关闭后将其保留良好,因此持久性单元将其视为不受管理的。 非托管实体不会被跟踪,因此所做的任何更改都不会反映在数据库中。 这意味着pt.getTaglie()。add(tg); 代码没有在entityManager知道的事情上完成。

通过使用getReference或find api,您将检索实体的托管实例,以便EntityManager可以跟踪对其所做的任何更改。 您也可以替换getReference和pianoTaglieDao.persist(tg);。 行带有pianoTaglieDao.merge(old)调用,它将对旧PianoTaglia所做的更改合并回持久性单元。 最好使用getReference或查找而不是缓存非托管实体,以帮助减少陈旧数据的覆盖,这可能更好。 缓存的对象可能不会反映最新的更改,然后可能会被合并调用覆盖,并且为了提高性能,它使您以后可以将应用程序扩展到多个线程和服务器,而无需进行大量更改。

您好,我不确定您在数据库中有哪些字段,但尝试添加

@JoinColumn("user_id")

下一行

@ManyToOne
private PianoTaglia pianoTaglia;

结果将是

@ManyToOne
@JoinColumn("pianoTagliaId")
private PianoTaglia pianoTaglia;

暂无
暂无

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

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