确定它不是错误的对象,但由于我已经诊断出问题,因此它不是正确的参考。
我有一个编辑器,我正在编辑一个包含更多bean的另一个列表编辑器的对象。
我有一个TagCollectionProxy,它包含一个TagProxy对象列表。当我使用附加的List对象保存TagCollectionProxy时会发生此问题。我的实体定位器正在查找数据库中的对象并设置值,但之后我没有对该对象的引用。这是新的,我不知道为什么会发生这种情况。
当我调用save对象时,此调试消息。我在setValue上的Tag类中放了一条消息,你可以看到我在域实体上设置了正确的值。 id匹配。问题出现在我的dao类中的save(TagCollection)的下一条消息中。我正在迭代实体并打印值,但该实体的值没有改变。
2014-08-29T15:34:15.912-0500|INFO: Setting value Tag2asdfasdfasdfasdf for id 294 Reference cpbx.rialto.dms.Tag@1135696
2014-08-29T15:34:15.944-0500|INFO: Tag name => asdfasdf Tag value => Tag2 id => 294 ref => cpbx.rialto.dms.Tag@1893889
2014-08-29T15:34:15.944-0500|INFO: Id of tag is 294
提前感谢您的帮助,我一分钟都在失去头发。
这是保存对象的Activity。请注意,我正在保存包含标记列表的TagCollectionProxy。
public class TagCollectionEditActivity extends AbstractActivity<TagCollectionEditView> implements
TagCollectionEditView.Presenter {
private Receiver<Long> saveReceiver = new Receiver<Long>() {
@Override
public void onSuccess(Long response) {
logger.info("saved tagCollection with id of " + response);
clientFactory.getPlaceController().goTo(new TagCollectionsPlace());
}
public void onConstraintViolation(Set<ConstraintViolation<?>> violations) {
for (ConstraintViolation<?> violation : violations) {
logger.warning(violation.getMessage());
}
getDisplay().getEditorDriver().setConstraintViolations(violations);
}
public void onFailure(ServerFailure failure) {
logger.log(Level.SEVERE, "Failed to save tag collection " + failure.getMessage());
getDisplay().showError(failure);
}
};
private static final Logger logger = Logger.getLogger(TagCollectionEditActivity.class.getName());
private IClientFactory clientFactory;
private TagCollectionDaoRequest editContext = null;
public TagCollectionEditActivity(IClientFactory clientFactory) {
super(new TagCollectionEditView(clientFactory.getResources(), clientFactory.getEventBus()));
this.clientFactory = clientFactory;
}
@Override
protected void bindToView() {
getDisplay().setPresenter(this);
final TagCollectionEditPlace place = (TagCollectionEditPlace) getPlace();
Long tagCollectionId = place.getTagCollectionId();
if (tagCollectionId == null) {
createTagCollection();
} else {
findCollection(tagCollectionId);
}
}
private void findCollection(Long tagCollectionId) {
clientFactory.tagCollectionRequest().find(tagCollectionId).with(getEntityProperties())
.fire(new Receiver<TagCollectionProxy>() {
@Override
public void onSuccess(TagCollectionProxy tagCollection) {
editContext = clientFactory.tagCollectionRequest();
editContext.save(tagCollection).with(getDisplay().getEditorDriver().getPaths()).to(saveReceiver);
getDisplay().getEditorDriver().edit(tagCollection, editContext);
GWT.log("Context is " + editContext.hashCode());
}
});
}
private void createTagCollection() {
editContext = clientFactory.tagCollectionRequest();
TagCollectionProxy tagCollection = editContext.create(TagCollectionProxy.class);
editContext.save(tagCollection).with(getDisplay().getEditorDriver().getPaths()).to(saveReceiver);
tagCollection.setTags(new ArrayList<TagProxy>());
getDisplay().getEditorDriver().edit(tagCollection, editContext);
}
@Override
public void onSave() {
RequestContext context = getDisplay().getEditorDriver().flush();
context.fire();
}
public String[] getEntityProperties() {
return new String[] { "tags", "deviceFamily" };
}
@Override
public void onCancel() {
clientFactory.getPlaceController().goTo(new TagCollectionsPlace());
}
}
这是我的标签集合代理,我在其中定义了我的DomainEntityLocator
@ProxyFor(value = TagCollection.class, locator = DomainEntityLocator.class)
public interface TagCollectionProxy extends DomainEntityProxy {
public List<TagProxy> getTags();
public void setTags(List<TagProxy> tags);
public DeviceFamilyProxy getDeviceFamily();
public void setDeviceFamily(DeviceFamilyProxy deviceFamily);
}
这是我的定位器,它使用数据库使用JPA entityManager查找对象。
public class DomainEntityLocator extends Locator<DomainEntity, Long> {
private static EntityManager em = null;
static {
Context initCtx;
try {
initCtx = new InitialContext();
// perform JNDI lookup to obtain container-managed entity manager
em = (javax.persistence.EntityManager) initCtx.lookup("java:comp/env/persistence/DomainEntityManager");
} catch (NamingException e) {
throw new RuntimeException("Unable to get the domain entity manager");
}
}
public DomainEntityLocator() {
}
@Override
public DomainEntity create(Class<? extends DomainEntity> clazz) {
try {
return clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override
public DomainEntity find(Class<? extends DomainEntity> clazz, Long id) {
return em.find(clazz, id);
}
@Override
public Class<DomainEntity> getDomainType() {
return DomainEntity.class;
}
@Override
public Long getId(DomainEntity domainObject) {
return domainObject.getId();
}
@Override
public Class<Long> getIdType() {
return Long.class;
}
@Override
public Object getVersion(DomainEntity domainObject) {
return 0;
}
}
更新
经过一番调查后,我意识到服务器端的JPA应该归咎于我的问题。我得出结论,DomainEntityLocator正在获取实体,但未将其包装在事务中。因此DomainEntityLocator#find()方法将检索实体,ServiceLayer将修改实体上的值。我遇到的问题是find方法没有包装在事务中,并且由于我正在检索的实体管理器没有被管理,所以实体管理器不会刷新更改。因此,我没有看到价值的更新。
关于主题问题#2
是否存在向事务定位器添加事务管理的常见模式,以便在请求工厂调用setter之后它将持久保存对象。我曾经用一个简单的servlet过滤器做这个,但我正在处理的项目需要java ee来维护这种功能。由于ServiceLayer查找它的方式,我无法使DomainEntityLocator成为无状态bean。是否最好扩展RequestFactoryServlet并将doPost包装在一个tranasction中?这似乎是最合乎逻辑的想法。有什么建议?
再次感谢托马斯
答案 0 :(得分:2)
在服务器端,RequestFactory将使用您的定位器按其ID加载每个对象,因此将加载TagCollection
(其Tag
列表),每个Tag
将也可以使用DomainEntityLocator
加载(假设它们在@ProxyFor
中声明的方式)。
将在定位器返回的对象上调用setter,而不是TagCollection
的{{1}}列表中的对象。
您必须确保它们是相同的实例。
使用JPA,这意味着使用“在视图中打开会话”(又名“每个请求的会话”)模式。 JPA会话使用缓存,因此在整个HTTP请求中共享相同的会话将确保只要从getTags
加载或直接通过其ID加载Tag
的相同实例。
答案 1 :(得分:0)
以下是我用EJB和RequestFactory解决问题的方法。
在确定RequestFactory方法没有被包装在实体管理器的事务中之后,我想到了两个选择。一个是我可以将UserTransaction添加到实体定位器的查找和创建方法,但我决定将整个请求包装在一个事务中,这适用于我的情况,因为我的请求工厂方法应该几乎总是在事务中。
我扩展了requestfactory servlet并将一个UserTransaction添加到了doPost。
@WebServlet("/rf/secure/dao")
public class DmsRequestFactoryServlet extends RequestFactoryServlet {
private static final long serialVersionUID = -521670028586842819L;
private static final Logger logger = Logger.getLogger(DmsRequestFactoryServlet.class.getName());
@Resource
private UserTransaction tx;
public static class SimpleExceptionHandler implements ExceptionHandler {
@Override
public ServerFailure createServerFailure(Throwable throwable) {
logger.log(Level.SEVERE, "Unable to complete request", throwable);
return new ServerFailure("Server Error: " + (throwable == null ? null : throwable.getMessage()), throwable
.getClass().getName(), ExceptionUtils.getStackTrace(throwable), true);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
try {
tx.begin();
super.doPost(request, response);
tx.commit();
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed request ", e);
}
}
public DmsRequestFactoryServlet() {
super(new SimpleExceptionHandler());
}
}