0

I have two entities in in OneToMany Relationship:

The parent Entity:

@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;
    }
}

And Child entity:

@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;
    }
}

For manage my Entities i use this generic 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);
    }      
}

Specialized for each class (this is PianoTagliaDao but TagliaDao is the same)

@Repository
public class PianoTaglieDao extends JpaDAO<PianoTaglia> {

}

When I create a PianoTaglia I keep a reference to the object with the generated ID... So i can navigate through my application and at any time i can create a Taglia. When i create a Taglia i use the reference to PianoTaglia, previusly created, in this way:

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
[...]

If i check the tables into DB is all ok! All the tables are well populated! But if i try to get all PianoTaglia the taglie collections are always empty:

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

after testing i've found the solution: when i create taglia i have to keep a new reference of 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
[...]

In this way when i keep the PianoTaglia Objects the taglie collections are well Populated..

My question is: Why JPA have this behaviour?

4

2 回答 2

3

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

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

于 2013-11-05T12:45:11.630 回答
0

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

@JoinColumn("user_id")

下一行

@ManyToOne
private PianoTaglia pianoTaglia;

结果将是

@ManyToOne
@JoinColumn("pianoTagliaId")
private PianoTaglia pianoTaglia;
于 2013-11-05T10:11:01.710 回答