抱歉迟到了!
和其他任何swing开发人员一样,我想我们都遇到了这样的问题,当JPA被合并时,我们希望处理所有的持久性方面,通过将所有的逻辑封装在一个单独的层中,同时促进更干净的关注点分离,相信它是完全免费的…但事实是,它绝对不是。
如您之前所述,分离实体存在一个问题,这使得我们需要创建解决此问题的变通方法。问题不仅在于使用惰性集合,还在于使用实体本身,首先,我们对实体所做的任何更改都必须反映到存储库中(使用分离的集合,这是不会发生的)。我不是这方面的专家。。但我将努力强调我对这一点的想法,并公开几个解决方案(其中许多方案以前已经被其他人宣布)。
从表示层(即驻留所有用户界面和交互的代码,这包括控制器)我们访问存储库层来执行简单的CRUD操作,尽管有特定的存储库和特定的表示,我认为这是社区接受的标准事实[我想这是罗伯特·马丁在DDD的一本书中写得很好的一个概念]
因此,基本上人们可以徘徊“如果我的实体是分离的,为什么我不让它附加”这样做,它将保持与我的存储库同步,对实体所做的所有更改都将“立即”反映到我的存储库中。是的。。。。这就是这个问题的第一个答案。。
1) 使用单个实体管理器对象,并使其从应用程序的开始一直保持打开状态。
-
乍一看,它似乎非常简单(实际上,只需打开EntityManager并全局存储其引用,并在应用程序中的任何地方访问相同的实例)
-
所以轻视它的简单,这不是最好的选择。。。。因此,让我们转到jpaapi提供的另一个解决方案。
-
-
您还必须考虑到所检索到的对象图太大,无法同时存储在内存中,因此可能会失败(正如克雷格所说)
再一次。。这并不能解决问题。
-
这以强制使用接口而不是普通域类为代价来解决问题。其实想得不错。。。但我也猜两者都不是标准的。我想我们都想使用域类。对于每个域对象,我们都必须编写一个接口。。。如果物体进入了。JAR。。。啊哈!触摸!我们不能在运行时提取接口:S,因此我们不能创建代理。
为了更好地解释这一点,我写了一个这样做的例子。。。
在域层(核心业务类所在的位置)
@Entity
public class Bill implements Serializable, BillInterface
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.ALL}, mappedBy="bill")
private Collection<Item> items = new HashSet<Item> ();
@Temporal(javax.persistence.TemporalType.DATE)
private Date date;
private String descrip;
@Override
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public void addItem (Item item)
{
item.setBill(this);
this.items.add(item);
}
public Collection<Item> getItems()
{
return items;
}
public void setItems(Collection<Item> items)
{
this.items = items;
}
public String getDescrip()
{
return descrip;
}
public void setDescrip(String descrip)
{
this.descrip = descrip;
}
public Date getDate()
{
return date;
}
public void setDate(Date date)
{
this.date = date;
}
@Override
public int hashCode()
{
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object)
{
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Bill))
{
return false;
}
Bill other = (Bill) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id)))
{
return false;
}
return true;
}
@Override
public String toString()
{
return "domain.model.Bill[ id=" + id + " ]";
}
public BigDecimal getTotalAmount () {
BigDecimal total = new BigDecimal(0);
for (Item item : items)
{
total = total.add(item.getAmount());
}
return total;
}
}
Item是另一个实体对象,它对账单的一个项目进行建模(一个账单可以包含许多项目,一个项目只属于一个并且只属于一个账单)。
BillInterface只是一个声明所有Bill方法的接口。
在持久层上,我放置BillProxy。。。
BillProxy的外观如下:
class BillProxy implements BillInterface
{
Bill bill; // protected so it can be used inside the BillRepository (take a look at the next class)
public BillProxy(Bill bill)
{
this.bill = bill;
this.setId(bill.getId());
this.setDate(bill.getDate());
this.setDescrip(bill.getDescrip());
this.setItems(bill.getItems());
}
@Override
public void addItem(Item item)
{
EntityManager em = null;
try
{
em = PersistenceUtil.createEntityManager();
this.bill = em.merge(this.bill); // attach the object
this.bill.addItem(item);
}
finally
{
if (em != null)
{
em.close();
}
}
}
@Override
public Collection<Item> getItems()
{
EntityManager em = null;
try
{
em = PersistenceUtil.createEntityManager();
this.bill = em.merge(this.bill); // attach the object
return this.bill.getItems();
}
finally
{
if (em != null)
{
em.close();
}
}
}
public Long getId()
{
return bill.getId(); // delegated
}
// More setters and getters are just delegated.
}
公共类DBBillRepository实现BillRepository
private EntityManagerFactory emf=null;
public DBBillRepository(EntityManagerFactory emf)
{
this.emf = emf;
}
private EntityManager createEntityManager()
{
return emf.createEntityManager();
}
@Override
public void create(BillInterface bill)
{
EntityManager em = null;
try
{
em = createEntityManager();
em.getTransaction().begin();
bill = ensureReference (bill);
em.persist(bill);
em.getTransaction().commit();
}
finally
{
if (em != null)
{
em.close();
}
}
}
@Override
public void update(BillInterface bill) throws NonexistentEntityException, Exception
{
EntityManager em = null;
try
{
em = createEntityManager();
em.getTransaction().begin();
bill = ensureReference (bill);
bill = em.merge(bill);
em.getTransaction().commit();
}
catch (Exception ex)
{
String msg = ex.getLocalizedMessage();
if (msg == null || msg.length() == 0)
{
Long id = bill.getId();
if (find(id) == null)
{
throw new NonexistentEntityException("The bill with id " + id + " no longer exists.");
}
}
throw ex;
}
finally
{
if (em != null)
{
em.close();
}
}
}
@Override
public void destroy(Long id) throws NonexistentEntityException
{
EntityManager em = null;
try
{
em = createEntityManager();
em.getTransaction().begin();
Bill bill;
try
{
bill = em.getReference(Bill.class, id);
bill.getId();
}
catch (EntityNotFoundException enfe)
{
throw new NonexistentEntityException("The bill with id " + id + " no longer exists.", enfe);
}
em.remove(bill);
em.getTransaction().commit();
}
finally
{
if (em != null)
{
em.close();
}
}
}
@Override
public boolean createOrUpdate (BillInterface bill)
{
if (bill.getId() == null)
{
create(bill);
return true;
}
else
{
try
{
update(bill);
return false;
}
catch (Exception e)
{
throw new IllegalStateException(e.getMessage(), e);
}
}
}
@Override
public List<BillInterface> findEntities()
{
return findBillEntities(true, -1, -1);
}
@Override
public List<BillInterface> findEntities(int maxResults, int firstResult)
{
return findBillEntities(false, maxResults, firstResult);
}
private List<BillInterface> findBillEntities(boolean all, int maxResults, int firstResult)
{
EntityManager em = createEntityManager();
try
{
Query q = em.createQuery("select object(o) from Bill as o");
if (!all)
{
q.setMaxResults(maxResults);
q.setFirstResult(firstResult);
}
List<Bill> bills = q.getResultList();
List<BillInterface> res = new ArrayList<BillInterface> (bills.size());
for (Bill bill : bills)
{
res.add(new BillProxy(bill));
}
return res;
}
finally
{
em.close();
}
}
@Override
public BillInterface find(Long id)
{
EntityManager em = createEntityManager();
try
{
return new BillProxy(em.find(Bill.class, id));
}
finally
{
em.close();
}
}
@Override
public int getCount()
{
EntityManager em = createEntityManager();
try
{
Query q = em.createQuery("select count(o) from Bill as o");
return ((Long) q.getSingleResult()).intValue();
}
finally
{
em.close();
}
}
private Bill ensureReference (BillInterface bill) {
if (bill instanceof BillProxy) {
return ((BillProxy)bill).bill;
}
else
return (Bill) bill;
}
}
正如您所注意到的,这个类实际上被称为DBBillRepository。。。这是因为可以有多个存储库(内存、文件、网络、?)类型和其他层的存储库,而不需要知道我在使用哪种类型的存储库。
ensureReference
用于获取真实bill对象的内部方法,仅适用于从表示层传递代理对象的情况。谈到表示层,我们只使用Bill接口,而不是Bill an all。
在某些控制器类(或者在SWING应用程序中的回调方法)中,我们可以按以下方式工作。。。
BillInterface bill = RepositoryFactory.getBillRepository().find(1L);
bill.addItem(new Item(...)); // this will call the method of the proxy
Date date = bill.getDate(); // this will deleagte the call to the proxied object "hidden' behind the proxy.
bill.setDate(new Date()); // idem before
RepositoryFactory.getBillRepository().update(bill);
4) 实际上还有一件事我们可以避免使用接口。。。使用某种退化的代理对象。。。
我们可以这样写BillProxy:
class BillProxy extends Bill
{
Bill bill;
public BillProxy (Bill bill)
{
this.bill = bill;
this.setId(bill.getId());
this.setDate(bill.getDate());
this.setDescrip(bill.getDescrip());
this.setItems(bill.getItems());
}
@Override
public void addItem(Item item)
{
EntityManager em = null;
try
{
em = PersistenceUtil.createEntityManager();
this.bill = em.merge(this.bill);
this.bill.addItem(item);
}
finally
{
if (em != null)
{
em.close();
}
}
}
@Override
public Collection<Item> getItems()
{
EntityManager em = null;
try
{
em = PersistenceUtil.createEntityManager();
this.bill = em.merge(this.bill);
return this.bill.getItems();
}
finally
{
if (em != null)
{
em.close();
}
}
}
}
还有,有几个帖子解释了同样的事情,读起来很有趣。
此外,我将任命这个参考,我仍然没有完全阅读,但看起来很有希望。
http://javanotepad.blogspot.com/2007/08/managing-jpa-entitymanager-lifecycle.html
http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/transactions.html
好吧,我们到了答案的结尾。。。我知道读这些东西太长了,可能有点痛苦:D(因为我的语法错误而变得更复杂了jeje),但无论如何,我希望它能帮助我们找到一个更稳定的解决方案,解决一个我们无法抹去jeje的问题。
问候语。
胜利者!!!