每当我进行连续保存时,我都会收到一个TransientObjectException;第一次保存或刷新屏幕/页面时,没有出现此错误。这让我很困惑,因为成功保存对象后,它就不应是瞬态的,因为它在我的数据库中有一个表示形式。
我下面的文件(User和UserPasswordInfo)具有一对多关系,其中一个User可以具有多个UserPasswordInfo(或UPI),并且通过我的用户的主键进行链接: user_id 。
我很确定问题出在以下文件中,而不是在其他地方,因为TOE错误仅在添加此文件后出现,并且据我所知,该软件在其生命周期(几年)中从未出现此问题。
这是我的休眠文件:
User.hbm.xml
<hibernate-mapping>
<class name="com.app.common.domain.User" table="users" >
<id name="id" type="java.lang.Integer">
<column name="user_id" />
<generator class="identity" />
</id>
<set name="passwordInfos" table="user_password_info" lazy="false" cascade="all" fetch="select">
<key>
<column name="user_id"></column>
</key>
<one-to-many class="com.app.common.domain.UserPasswordInfo" />
</set>
</class>
</hibernate-mapping>
UserPasswordInfo.hbm.xml
<hibernate-mapping>
<class name="com.app.common.domain.UserPasswordInfo" table="user_password_info">
<id name="passwordInfoId" type="java.lang.Integer">
<column name="password_info_id" />
<generator class="identity" />
</id>
<property name="password" column="password" type="java.lang.String"/>
<property name="passwordTS" column="password_ts" type="java.sql.Timestamp"/>
<property name="pwdChange" column="initial_password_change" type="java.lang.Boolean"/>
<property name="userId" column="user_id" type="java.lang.Integer"/>
</class>
</hibernate-mapping>
这是他们的Java文件:
User.java
import java.sql.Timestamp;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
public class User extends DBObject {//DBObject has a getter and setter for the id
private Set<UserPasswordInfo> passwordInfos = new HashSet<UserPasswordInfo>(0);
public User() {
super();
}
.
.
.
public Set<UserPasswordInfo> getPasswordInfos() {
return passwordInfos;
}
public void setPasswordInfos(Set<UserPasswordInfo> passwordInfos) {
this.passwordInfos = passwordInfos;
}
public void addUPI(String pwd, Timestamp pwdTS, Boolean pwdChng){
UserPasswordInfo upi = new UserPasswordInfo();
upi.setPassword(pwd);
upi.setPasswordTS(pwdTS);
upi.setPwdChange(pwdChng);
if(getId() != null)
upi.setUserId(getId().intValue());
passwordInfos.add(upi);
}
public String getLatestPassword(){
return getSortedList().get(0).getPassword();
}
public UserPasswordInfo getLatestUPI(){
ArrayList<UserPasswordInfo> list = getSortedList();
return (list == null || list.size() == 0) ? null : list.get(0);
}
public ArrayList<String> getLastThreePasswords(){
final ArrayList<UserPasswordInfo> upiList = getSortedList();
if(upiList == null)
return null;
ArrayList<String> list = new ArrayList<String>();
int i = 0;
while(i < 3 && i < list.size()){
list.add(upiList.get(i++).getPassword());
}
return list;
}
public ArrayList<UserPasswordInfo> getSortedList(){
ArrayList<UserPasswordInfo> upi = new ArrayList<UserPasswordInfo>(getPasswordInfos());
if(upi == null || upi.size() == 0){
return null;
}
Collections.sort(upi, new Comparator<UserPasswordInfo>() {
public int compare(UserPasswordInfo o1, UserPasswordInfo o2) {
if (o1.getPasswordTS() == null || o2.getPasswordTS() == null)
return 0;
return o2.getPasswordTS().compareTo(o1.getPasswordTS());
}
});
return upi;
}
}
UserPasswordInfo.java
import java.sql.Timestamp;
public class UserPasswordInfo extends DBObject{
private int passwordInfoId;
private String password;
private Timestamp passwordTS;
private Boolean pwdChange;
private int userId;
public UserPasswordInfo(){}
public UserPasswordInfo(String pwd, Timestamp pwdTS, Boolean pwdChng){
password = pwd;
passwordTS = pwdTS;
pwdChange = pwdChng;
}
public UserPasswordInfo(int userId, String pwd, Timestamp pwdTS, Boolean pwdChng){
this.userId = userId;
password = pwd;
passwordTS = pwdTS;
pwdChange = pwdChng;
}
public int getPasswordInfoId(){
return passwordInfoId;
}
public void setPasswordInfoId(int pid){
passwordInfoId = pid;
}
public String getPassword(){
return password;
}
public void setPassword(String pwd){
password = pwd;
}
public Timestamp getPasswordTS(){
return passwordTS;
}
public void setPasswordTS(Timestamp ts){
passwordTS = ts;
}
public void setPwdChange(Boolean pwdChange) {
this.pwdChange = pwdChange;
}
public Boolean getPwdChange() {
return pwdChange;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
这是错误消息:
ERROR BeanPopulator -
propertyName=passwordInfos
readerMethod=public java.util.Set com.app.domain.User.getPasswordInfos()
setterMethod=public void com.app.common.domain.User.setPasswordInfos(java.util.Set)
fromBean=[User someUser, id: 268, role: front desk]
toBean=[User , id: null]
net.sf.gilead.exception.TransientObjectException
at net.sf.gilead.core.hibernate.HibernateUtil.getId(HibernateUtil.java:316)
at net.sf.gilead.core.hibernate.HibernateUtil.getId(HibernateUtil.java:224)
at net.sf.gilead.core.hibernate.HibernateUtil.loadPersistentCollection(HibernateUtil.java:854)
at net.sf.gilead.core.hibernate.HibernateUtil.createPersistentCollection(HibernateUtil.java:843)
at net.sf.gilead.core.beanlib.merge.MergeCollectionReplicator.replicateCollection(MergeCollectionReplicator.java:119)
at net.sf.beanlib.provider.replicator.ReplicatorTemplate.replicate(ReplicatorTemplate.java:112)
at net.sf.beanlib.provider.BeanTransformer.transform(BeanTransformer.java:231)
at net.sf.beanlib.provider.BeanPopulator.doit(BeanPopulator.java:201)
at net.sf.beanlib.provider.BeanPopulator.processSetterMethod(BeanPopulator.java:172)
at net.sf.beanlib.provider.BeanPopulator.populate(BeanPopulator.java:269)
at net.sf.gilead.core.LazyKiller.populate(LazyKiller.java:288)
at net.sf.gilead.core.LazyKiller.attach(LazyKiller.java:237)
at net.sf.gilead.core.PersistentBeanManager.mergePojo(PersistentBeanManager.java:554)
at net.sf.gilead.core.PersistentBeanManager.merge(PersistentBeanManager.java:318)
at net.sf.gilead.core.PersistentBeanManager.mergeCollection(PersistentBeanManager.java:581)
at net.sf.gilead.core.PersistentBeanManager.merge(PersistentBeanManager.java:290)
at net.sf.gilead.gwt.GileadRPCHelper.parseInputParameters(GileadRPCHelper.java:94)
at net.sf.gilead.gwt.GileadRPCHelper.parseInputParameters(GileadRPCHelper.java:137)
at net.sf.gilead.gwt.PersistentRemoteService.processCall(PersistentRemoteService.java:172)
编辑以下是一些可能与此错误相关的链接和代码部分:
这是在我保存对象时进行的方法调用(在调用此方法之前引发了以上错误)
@SuppressWarnings({ "rawtypes" })
private List saveObjectList(List<? extends DBObject> values) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
for (DBObject value : values) {
if (value.getIsDirty()) {
Integer clientId = value.getClientId();
if (value.getId() != null) {//enters here
value = (DBObject) session.merge(value);
session.persist(value);
}
else {
session.save(value);
}
value.setClientId(clientId);
value.setIsDirty(false);
}
}
tx.commit();//fails here
return values;
}
catch (Exception ex) {
String msg = "Failed to save values. ";
if (values == null) {
msg += " Values to save were null.";
}
else {
msg += " Values to save had length: " + values.size();
}
rollbackOrCloseSession(tx, session, "saveObjectList() - " + msg, ex);
return null;
}
}
GileadRPCHelper
/**
* Parse RPC input parameters.
* Must be called before GWT service invocation.
* @param rpcRequest the input GWT RPC request
* @param beanManager the Hibernate bean manager
* @param session the HTTP session (for HTTP Pojo store)
*/
public static void parseInputParameters(Object[] parameters,
PersistentBeanManager beanManager,
HttpSession session)
{
// Init classloader for proxy mode
//
if (beanManager.getClassMapper() instanceof ProxyClassMapper)
{
initClassLoader();
}
// Set HTTP session of Pojo store in thread local
//
HttpSessionProxyStore.setHttpSession(session);
// Merge parameters if needed
//
if (parameters != null)
{
long start = System.currentTimeMillis();
for (int index = 0 ; index < parameters.length; index ++)
{
if (parameters[index] != null)
{
try
{
//***ERROR occurs when this is called
parameters[index] = beanManager.merge(parameters[index], true);
}
catch (NotAssignableException ex)
{
log.debug(parameters[index] + " not assignable");
}
catch (TransientObjectException ex)
{
log.info(parameters[index] + " is transient : cannot merge...");
}
}
}
if (log.isDebugEnabled())
{
log.debug("Merge took " + (System.currentTimeMillis() - start) + " ms.");
}
}
}
PersistentBeanManager
/**
* Merge the clone POJO to its Hibernate counterpart
*/
public Object merge(Object object) {
// Explicit merge
return merge(object, false);
}
/**
* Merge the clone POJO to its Hibernate counterpart
*/
@SuppressWarnings("unchecked")
public Object merge(Object object, boolean assignable) {
// Precondition checking
//
if (object == null) {
return null;
}
if (_persistenceUtil == null) {
throw new RuntimeException("No Persistence Util set !");
}
// Collection handling
//
if (object instanceof Collection) {
return mergeCollection((Collection) object, assignable);
} else if (object instanceof Map) {
return mergeMap((Map) object, assignable);
} else if (object.getClass().isArray()) {
// Check primitive type
//
if (object.getClass().getComponentType().isPrimitive()) {
return object;
}
// Merge as a collection
//
Object[] array = (Object[]) object;
Collection result = mergeCollection(Arrays.asList(array), assignable);
// Get the result as an array (much more tricky !!!)
//
Class<?> componentType = object.getClass().getComponentType();
Object[] copy = (Object[]) java.lang.reflect.Array.newInstance(componentType, array.length);
return result.toArray(copy);
} else {
return mergePojo(object, assignable);
}
}
/**
* Retrieve the Hibernate Pojo and merge the modification from GWT
*
* @param clonePojo the clone pojo
* @param assignable does the source and target class must be assignable
* @return the merged Hibernate POJO
* @exception UnsupportedOperationException if the clone POJO does not implements ILightEntity and the POJO store is
* stateless
* @exception NotAssignableException if source and target class are not assignable
*/
protected Object mergePojo(Object clonePojo, boolean assignable) {
// Get Hibernate associated class
Class<?> cloneClass = clonePojo.getClass();
Class<?> hibernateClass = null;
if (_classMapper != null) {
hibernateClass = _classMapper.getSourceClass(cloneClass);
}
if (hibernateClass == null) {
// Not a clone : take the inner class
hibernateClass = clonePojo.getClass();
}
// Precondition checking : is the pojo managed by Hibernate
if (_persistenceUtil.isPersistentClass(hibernateClass) == true) {
// Assignation checking
if ((assignable == true) && (hibernateClass.isAssignableFrom(cloneClass) == false)) {
throw new NotAssignableException(hibernateClass, cloneClass);
}
}
// Retrieve the pojo
try {
Serializable id = null;
try {
id = _persistenceUtil.getId(clonePojo, hibernateClass);
if (id == null) {
_log.info("HibernatePOJO not found : can be transient or deleted data : " + clonePojo);
}
} catch (TransientObjectException ex) {
_log.info("Transient object : " + clonePojo);
} catch (NotPersistentObjectException ex) {
if (holdPersistentObject(clonePojo) == false) {
// Do not merge not persistent instance, since they do not
// necessary
// implement the Java bean specification
//
if (_log.isDebugEnabled()) {
_log.debug("Not persistent object, merge is not needed : " + clonePojo);
}
return clonePojo;
} else {
if (_log.isDebugEnabled()) {
_log.debug("Merging wrapper object : " + clonePojo);
}
}
}
if (ClassUtils.immutable(hibernateClass)) {
// Do not clone immutable types
//
return clonePojo;
}
// Create a new POJO instance
//
Object hibernatePojo = null;
try {
if (AnnotationsManager.hasGileadAnnotations(hibernateClass)) {
if (id != null) {
// ServerOnly or ReadOnly annotation : load from DB
// needed
//
hibernatePojo = _persistenceUtil.load(id, hibernateClass);
} else {
// Transient instance
//
hibernatePojo = clonePojo;
}
} else {
Constructor<?> constructor = hibernateClass.getDeclaredConstructor(new Class<?>[] {});
constructor.setAccessible(true);
hibernatePojo = constructor.newInstance();
}
} catch (Exception e) {
throw new RuntimeException("Cannot create a fresh new instance of the class " + hibernateClass, e);
}
// Merge the modification in the Hibernate Pojo
//
_lazyKiller.attach(hibernatePojo, clonePojo);
return hibernatePojo;
} finally {
_persistenceUtil.closeCurrentSession();
_proxyStore.cleanUp();
}
}
BeanPopulator
@Override
public <T> T populate() {
if (getBeanTransformerSpi() != null) {
getBeanTransformerSpi().getClonedMap().put(fromBean, toBean);
}
// invoking all declaring setter methods of toBean from all matching getter methods of fromBean
for (Method m : baseConfig.getSetterMethodCollector().collect(toBean)) {
processSetterMethod(m);
}
@SuppressWarnings("unchecked")
T ret = (T) toBean;
return ret;
}
private void processSetterMethod(Method setterMethod) {
String methodName = setterMethod.getName();
final String propertyString = methodName.substring(baseConfig.getSetterMethodCollector().getMethodPrefix().length());
if (baseConfig.isDebug()) {
if (log.isInfoEnabled()) {
log.info(new StringBuilder("processSetterMethod: processing propertyString=").append(propertyString).append("")
.append(", fromClass=").append(fromBean.getClass()).append(", toClass=").append(toBean.getClass()).toString());
}
}
Method readerMethod = baseConfig.getReaderMethodFinder().find(propertyString, fromBean);
if (readerMethod == null) {
return;
}
// Reader method of fromBean found
Class<?> paramType = setterMethod.getParameterTypes()[0];
String propertyName = Introspector.decapitalize(propertyString);
try {
doit(setterMethod, readerMethod, paramType, propertyName);
} catch (Exception ex) {
baseConfig.getBeanPopulationExceptionHandler().initFromBean(fromBean).initToBean(toBean).initPropertyName(propertyName)
.initReaderMethod(readerMethod).initSetterMethod(setterMethod).handleException(ex, log);
}
}
private <T> void doit(Method setterMethod, Method readerMethod, Class<T> paramType, final String propertyName) {
if (baseConfig.getDetailedPropertyFilter() != null) {
if (!baseConfig.getDetailedPropertyFilter().propagate(propertyName, fromBean, readerMethod, toBean, setterMethod)) {
return;
}
}
if (baseConfig.getPropertyFilter() != null) {
if (!baseConfig.getPropertyFilter().propagate(propertyName, readerMethod)) {
return;
}
}
Object propertyValue = this.invokeMethodAsPrivileged(fromBean, readerMethod, null);
if (baseConfig.getBeanSourceHandler() != null) {
baseConfig.getBeanSourceHandler().handleBeanSource(fromBean, readerMethod, propertyValue);
}
if (transformer != null) {
PropertyInfo propertyInfo = new PropertyInfo(propertyName, fromBean, toBean);
propertyValue = transformer.transform(propertyValue, paramType, propertyInfo);
}
if (baseConfig.isDebug()) {
if (log.isInfoEnabled()) {
log.info("processSetterMethod: setting propertyName=" + propertyName);
}
}
// Invoke setter method
Object[] args = { propertyValue };
this.invokeMethodAsPrivileged(toBean, setterMethod, args);
return;
}
Gilead HibernateUtil class
最佳答案
可能这是由以下代码引起的:
public void addUPI(String pwd, Timestamp pwdTS, Boolean pwdChng){
UserPasswordInfo upi = new UserPasswordInfo();
upi.setPassword(pwd);
upi.setPasswordTS(pwdTS);
upi.setPwdChange(pwdChng);
if(getId() != null)
upi.setUserId(getId().intValue());
passwordInfos.add(upi);
}
如果调用此方法,然后存储
User
对象,则假定会引发异常。关键是您需要先将UserPasswordInfo
附加到上下文,然后才可以将其添加到User
对象。因此正确的顺序是先存储UserPasswordInfo:
entityManager.persist(upi);
然后将其添加到密码中并存储用户对象:
user.getPasswordInfos().add(upi);
entityManager.save(user);