我需要一些关于在Java中设计N层系统的“集成层”的建议。此层负责持久保存和检索“业务层”(位于单独的服务器上)的数据。我是J2EE新手,我读了几本书和博客。技术首字母缩略词的字母汤令我困惑,所以我有一些问题。
首先,我到目前为止:我正在使用JPA(通过Hibernate)来持久化并将数据检索到数据库。我创建了数据访问对象EJB并计划部署到应用程序服务器(JBoss),这使得事务更容易(它们在我的DAO的功能级别)并且我不必担心获取EntityManager的句柄(依赖注入)。这是一个事物的例子:
@Entity
class A{
@Id
Long id;
@OneToMany
List<B> setOfBs = new ArrayList<B>;
}
@Entity
class B{
@Id
Long id;
}
@Remote
public interface ADAO{
public A getAById(Long id);
}
@Stateless
class ADAOImpl implements ADAO{
@PersistenceContext
EntityManager em;
public A getAById(Long id){ ... }
}
我的问题:业务层应该如何与Integration Tier交换数据。我已经阅读了RESTful服务,它们看起来很简单。我关心的是当获取和设置的频率增加时的性能(HTTP通信似乎不是特别快)。另一种选择是RMI。我的DAO已经是EJB了。我可以让业务层直接访问它们(通过JNDI)吗?如果是这样,如果上面示例中的@OneToMany链接被延迟加载会发生什么?
例如,如果业务层执行以下操作:
Context context = new InitialContext(propertiesForIntegrationTierLookup);
ADAOImpl aDao = (ADAOImpl) context.lookup("something");
A myA = aDao.getAById(0);
int numberOfBs = myA.setOfBs.size();
如果懒惰地加载setOfBs列表,当业务层(在单独的服务器上)访问列表时,大小是否正确?列表是否以某种方式通过EJB的魔力正确加载?如果不是(我期望),解决方案是什么?
对不起,很长的帖子。就像我说我是J2EE的新手,我已经阅读了足够的内容以获得一般性的想法,但我需要帮助将这些部分组合在一起。
答案 0 :(得分:0)
当您在延迟集合上调用size()时,它会被初始化,因此无论您使用哪个界面 - 远程或本地,您都将始终获得正确的大小。
另一种情况是当您尝试将JPA类用作数据传输对象(DTO)并通过远程接口请求它们时。我不记得任何延迟初始化问题,因为在传输之前所有对象都必须在服务器端序列化(使用惰性集合初始化)。结果,整个对象图通过网络传递,这可能导致严重的CPU和网络开销。此外,为了实现反序列化,您必须与远程应用程序共享JPA类。这就是'EJB魔术'结束的地方和方式:)
因此,一旦可以进行远程调用,我建议开始考虑将数据传输策略和非JPA数据传输对象作为附加数据层。就我而言,我已经为XML绑定(JAXB)注释了DTO类,并在Web服务中重用它们。
答案 1 :(得分:0)
简短回答:如果您使用的是“集成层”方法,那么您应该集成的内容应该是遵循SOA原则的松散耦合服务。
这意味着您不应该允许远程调用可能在另一台服务器上调用框架的实体上的方法。如果你这样做,你实际上是在构建一个紧密耦合的分布式应用程序,你将不得不担心延迟加载问题和持久化上下文的范围。如果您需要,可以考虑扩展持久性上下文http://docs.jboss.org/ejb3/docs/tutorial/extended_pc/extended.html。
您已经谈到了“业务层”,但JPA没有提供业务层。它提供实体并允许CRUD操作,但这些通常不是业务操作。 “RegisterUser”操作不仅仅是持久化“用户”实体的问题。您的DAO层可能提供更高级别的操作,但DAO通常用于在数据库上放置一个薄层,但它仍然非常以数据为中心。
更好的方法是定义业务服务类型操作并使这些操作成为您公开的服务。您可能需要在DAO之上添加另一个图层,或者您可能希望拥有一个图层(转换DAO图层)。
您的业务层应该调用flush并处理任何JPA异常并将其全部隐藏在调用者中。
如何传输数据的问题依然存在。在许多情况下,您的业务服务请求的参数将与您的JPA实体类似,但我认为您会注意到,您希望定义新的DTO通常存在足够的差异。例如,“RegisterUser”业务操作可能会同时更新“User”和“EmailAddresses”表。 User表可能包含“createdDate”属性,该属性不属于“RegisterUser”操作,但设置为当前日期。
要创建DTO,您可能希望查看Project Lombok。
要将DTO复制到实体,您可以使用Apache Commons BeanUtils(例如,PropertyUtils.copyProperties)执行大量的工作,如果属性名称相同,则可以正常工作。
就个人而言,在这种情况下,我没有看到XML的重点,除非你想完全解耦你的实现。