编写多层应用程序时,最好只将对象ID传递给事务服务方法。但我宁愿传递实际的JPA对象。与问题Is domain model object passing between layers overhead?不同,一些同事担心a)对象可能属于另一个/无事务,如果因此在服务方法中修改时会导致问题,并且b)在调用此类服务方法后修改对象时可能会导致问题UI组件,因为事务已经提交。
在代码中表示我更愿意拥有
@Named public class MyServiceImpl
{
...
@Transactional
public BigDecimal calculate(ObjectOne objectOne, ObjectTwo objectTwo)
{
...
}
}
而不是
@Named public class MyServiceImpl
{
...
@Transactional
public BigDecimal calculate(long objectOneId, long objectTwoId)
{
ObjectOne objectOne = objectOneService.find(objectOneId);
ObjectTwo objectTwo = objectTwoService.find(objectTwoId);
...
}
}
有没有一种技术,事务管理器(spring)正确关注对象?或者,您是否建议使用JPA merge或其他任何显式来正确处理直接对象引用?或者你也不鼓励传递对象而不是ID?
特别是带有官方或众所周知的来源链接的解释显然会有所帮助。
答案 0 :(得分:2)
由于default behavior of @Transactional是PROPAGATION_REQUIRED,将业务对象传递给事务方法应该没问题。如果调用操作已打开事务,则打开新的虚拟事务(对于同一物理事务),并且对象引用保持不变。如果没有事务处于活动状态,则不会损害持久状态。
为了确保对象是理智的,你可以做到
@Transactional
public BigDecimal calculate(ObjectOne objectOne, ObjectTwo objectTwo)
{
objectOne = objectOneService.find(objectOne.getId());
objectTwo = objectTwoService.find(objectTwo.getId());
...
}
这很便宜,因为从缓存中提取对象。但由于对象属于同一物理事务,因此不需要它。
如果对象确实来自不同的事务,您可以使用上面的代码解决它。但是您的调用方法将不会看到对该对象所做的任何更改,除非它再次从数据库中获取其对象(并且取决于隔离开始新事务)。
答案 1 :(得分:1)
根据我的经验,我倾向于传递对象而不是id。根据您的示例,这是非常简单的场景。但实时,对象可能非常复杂并且具有许多子对象。在这种情况下,每次检索对象都会妨碍性能。
根据情况而有所不同。在复杂对象的情况下,我更喜欢将对象存储在会话中并将其传递并将其合并到实体管理器中。
此外,它还取决于其他因素,例如您希望在会话中保留多少数据(如果传递完整对象),或者每次检索对象时可以承受多少性能降低。
答案 2 :(得分:0)
您对分离对象的概念并不清楚。有关JPA / hibernate中不同对象状态的信息,请参阅以下链接。
答案 3 :(得分:0)
根据我的经验,正确的方法是在中间:根据你的用例(即商业逻辑),你会想要一个或另一个。
使用ID的示例:
假设信贷审批人想要批准或拒绝信贷。为此,我肯定会去ID:
ApprovalService.approveCreditRequest(Long creditRequestId,ApprovalStatus approvalStatus);
在Web层中获取CreditRequest
的问题似乎对我没用,并且假设这不是问题,您必须在EJB /服务层中确保Web层向您发送正确加载的内容CreditRequest
实例,即网络层中没有人更改任何重要字段,例如creditRequest.setApprovalStatus(ApprovalStatus.DENY)
或creditRequest.setRequester(anotherRequester)
。而且也不要只考虑WebLayer,因为可能有一个调用您的服务层的远程Ejb,您将不得不信任。您肯定希望使用ID的另一个用例是在以下情形中:通过调用方法CreditRequests
,您有一个批处理批准许多(数万)approveCreditRequest()
的批处理。从DB获取所有实体及其所有关系(而不仅仅是其ID)肯定是性能瓶颈并且将每个实例传递给方法可以批准某些CreditRequests两次(例如,同时在实例之间获取和approveCreditRequest()
被称为,它已经被批准了。)
这种方法的缺点是:UnitTest
并不容易,你需要进行集成测试或模拟框架(例如mockito)。
使用完整对象的示例: 假设您需要在HTTP请求之后更改90%的字段,例如更新实体时。比使用整个对象更容易。
现在与实例的托管状态相关:当Entity实例离开服务层时(无论是EJB还是Spring Beans),您必须确保事务已关闭(在WebLayer中从不使用Transactional),因此实体将自动取消管理,以便您可以在WebLayer中无问题地更改它们。