非空属性引用持久值的空值或瞬态值

2022-09-03 06:57:29

我正在尝试使用JPA1和Hibernate实现来持久化两个不同的实体。其代码如下所示:

父实体类

@Entity
@Table(name = "parent")
public class Parent implements Serializable {

    {...}
    private Child child;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "child_id", nullable = "false")
    public Child getChild() {
        return child;
    }

    public void setChild(Child child) {
        this.child = child;
}

子实体类

@Entity
@Table(name = "child")
public class Child implements Serializable {

    private Integer id;

    @Id
    @Column(name = "child_id")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}

测试用例

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:META-INF/application.xml")
@Transactional
public class ParentTest extends TestCase {

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    public void testSave() {
        Child child = new Child();
        child.setId(1);

        Parent parent = new Parent();
        parent.setChild(child);

        entityManager.persist(parent.getChild());
        entityManager.persist(parent); // throws the exception
    }
}

应用程序上的实体管理器和事务.xml

<tx:annotation-driven transaction-manager="transactionManager" /> 

<jee:jndi-lookup id="dataSource" jndi-name="java:/jdbc/myds" expected-type="javax.sql.DataSource" /> 

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="packagesToScan" value="com.mypackage" />
    <property name="dataSource" ref="dataSource" /> 
    <property name="jpaVendorAdapter"› 
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect>org.hibernate.dialect.Oracle10gDialect</prop>
        </props>
    </property>
</bean> 

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean>

尝试插入父对象时,休眠会引发一个 PropertyValueException,指出子项为空或暂时性,即使子项是在此操作之前创建并保留的。奇怪的是,这仅在单元测试中失败,并且在实际应用程序中,使用预插入的子级,这按预期工作。

PS:我很清楚我可以用级联持久映射孩子,但这不是这里的想法。我只想检查这两者是否独立工作。


答案 1

这里的问题是,您正在使用设置的值来持久保存父表。当它要持久化时,它需要子表ID,该子表ID必须已经持久化,因为它是一个外键,因此它是一个非空属性引用空值。

    @Test
    public void testSave() {
        Child child = new Child();
        child.setId(1);
        entityManager.persist(child); 

        Parent parent = new Parent();
        parent.setChild(child);

        entityManager.persist(parent); 
    }

尝试这样做,首先保存孩子,然后保存父级。否则更改映射


答案 2

你在 Parent 中使用@ManyToOne的第一件事告诉我,你为一个子对象提供了多个父对象,我想情况并非如此。

根据您的类结构,我可以理解您在父实体和子实体之间有一对一映射。

关于例外情况,您是否有理由不使用父级和子级之间的级联来自动处理保存,更新和删除的映射?如果您还没有考虑过它,则可以通过设置以下配置来尝试一下,如下所示:示例父/子 - 休眠

cascade = {CascadeType.ALL}

虽然我建议将子项和父项之间的映射从 ManyToOne 更改为 OneToOne。

请让我知道。


推荐