了解JPA中的OneToMany关系

时间:2013-11-05 09:37:43

标签: java jpa eclipselink one-to-many

我在OneToMany关系中有两个实体:

父实体:

@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有这种行为?

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

嗨我不确定你在db中有哪些字段但是尝试添加

@JoinColumn("user_id")

代表下一行

@ManyToOne
private PianoTaglia pianoTaglia;

结果将是

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