双向一对一“对象引用未保存的瞬态实例”

时间:2015-02-07 20:00:10

标签: java spring hibernate jpa

我有一个简单的一对一关系:

  • 为每个实体分开DAO。
  • 交易由Spring管理。
 PersonDao personDao = ctx.getBean(PersonDao.class, "personDaoImpl");
 VehicleDao vehicleDao = ctx.getBean(VehicleDao.class, "vehicleDaoImpl");

 Vehicle vehicle = new Vehicle("Audi");
 Person person = new Person("Mike");

 vehicle.setPerson(person);
 person.setVehicle(vehicle); 

 personDao.save(person);
 vehicleDao.save(vehicle);

每当我运行应用程序时,我都会遇到以下异常:


Exception in thread "main"
org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.TransientPropertyValueException: object references an unsaved
mike.Person.vehicle -> mike.Vehicle; nested exception is
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance - save the transient instance
before flushing : mike.Person.vehicle -> mike.Vehicle

我尝试在两个订单中保存实体:

personDao.save(person);
vehicleDao.save(vehicle);

vehicleDao.save(vehicle);
personDao.save(person);

我得到同样的例外。

我能够通过以下方式解决这个问题:

  1. 使用级联。
  2. 我想OpenSessionInView也可以。
  3. 问题是,是否有更好的解决方案?也许我做了一些根本错误的事情?


    以下是(普通)实体和DAO:

    @Entity
    public class Person {
        @Id @GeneratedValue
        private int id;
        private String name;
        @OneToOne
        private Vehicle vehicle;
    
        /* getters, setters, constructors */
    }
    

    -

    @Entity
    public class Vehicle {
        @Id @GeneratedValue
        private int id;
        private String name;
        @OneToOne
        private Person person;
    
        /* getters, setters, constructors */
    }
    

    -

    @Repository
    public class PersonDaoImpl implements PersonDao {
    
        @PersistenceContext
        private EntityManager em;
    
        @Transactional
        public void save(Person p) {
            em.persist(p);
        }
    }
    

    -

    @Repository
    public class VehicleDaoImpl implements VehicleDao {
    
        @PersistenceContext
        private EntityManager em;
    
        @Transactional
        public void save(Vehicle v) {
            em.persist(v);
        }
    }
    

3 个答案:

答案 0 :(得分:2)

我收到了这个错误,直到我弄明白为什么这是一个令人头疼的问题......就像它说的那样

object references an unsaved transient instance   // Read it again

实际原因,你的对象 - 这里的外键映射对象是指在保存主键的表的主键字段中不可用的值(在执行dao操作时表中不可用)。因此,在对保存外键的模型类执行操作之前,您必须对主键表执行操作。

如果你对上段感到困惑,那么我会让它变得简短而又甜蜜

您的外键是指主键字段中不可用的值

通过

尝试sysout外键的值
System.out.println(modelClassObject.getForeignKeyGetter().getId());

我确定它会返回0或外键所指的主键字段中没有的值。

答案 1 :(得分:2)

您可以在单个事务中使用级联或持久化两个实体:

@Transactional
void savePersonVehiclePair(Person person, Vehicle vehicle){
    personDao.save(person);
    vehicleDao.save(vehicle);
}

答案 2 :(得分:0)

好吧,我刚刚面临同样的问题,所以想分享我如何处理异常。耐心地通过我的jsp后,我意识到我犯了一个粗心的错误: 而不是将path属性设置为“bioData.address.lga.lgaId”,如下面的代码片段所示,我不小心将其设置为“bioData.address.lga.lgaName”。所以lgaId的主键没有映射到lga对象,所以没有办法将键映射到对象。

<form:select path="bioData.address.lga.lgaId" id="title" required="required" class="form-control col-md-7 col-xs-12">

                                                 <c:forEach items="${lgaList}" var="lga">
                                                    <form:option value="${lga.lgaId }"><c:out value="${lga.name}"/></form:option>
                                                </c:forEach>

                                            </form:select>

下面的代码是错误的代码:

<form:select path="bioData.address.lga.lgaName" id="title" required="required" class="form-control col-md-7 col-xs-12">

                                                 <c:forEach items="${lgaList}" var="lga">
                                                    <form:option value="${lga.lgaId }"><c:out value="${lga.name}"/></form:option>
                                                </c:forEach>

                                            </form:select>

现在工作正常。