当前位置: 首页 > 知识库问答 >
问题:

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

周奇文
2023-03-14

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

  • 每个实体都有单独的DAO
 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也会工作。

问题是,是否有更好的解决方案?也许我做错了什么?

以下是(琐碎的)实体和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个答案

汪德寿
2023-03-14

嗯,我只是面临着同样的问题,所以想分享我是如何处理例外的。在耐心地浏览了我的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>

它现在运转良好。

沈英勋
2023-03-14

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

@Transactional
void savePersonVehiclePair(Person person, Vehicle vehicle){
    personDao.save(person);
    vehicleDao.save(vehicle);
}
单喜
2023-03-14

我犯了这个错误,这是一个很大的头疼,直到我弄明白了原因。。就像上面说的

object references an unsaved transient instance   // Read it again

实际原因是,您的对象——这里的外键映射对象指的是一个值,该值在保存主键的表的主键字段中不可用(在执行dao操作时在表中不可用)。所以,在对持有外键的模型类执行操作之前,必须先对主键表执行操作。。

如果你被上面的段落弄糊涂了,我会把它变得简短而甜蜜

外键引用的值在主键字段中不可用

请通过以下方式尝试sysout外键的值:

System.out.println(modelClassObject.getForeignKeyGetter().getId());

我确信它将返回0或外键引用的主键字段中不可用的值。

 类似资料: