休眠:orphanRemoval无法用于克隆对象

时间:2019-02-24 02:08:08

标签: java hibernate jpa jackson hibernate-cascade

我在使用休眠持久化克隆对象时遇到问题。删除其嵌套子级后,该记录并未从数据库中删除(我已将orphanRemoval = true)。

如以下代码所示,将使用json创建一个克隆。在clone.removeItem()之后,休眠状态应在session.update(clone)时删除孤立项。但是当我再次查询时它仍然存在。怎么了?

import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;


public class JacksonTest {

    SessionFactory factory;

    @BeforeClass
    public void setup() {

        StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure("hibernate.cfg.xml")
                .build();
        factory = new MetadataSources(registry)
                .addAnnotatedClass(PurchaseOrder.class)
                .addAnnotatedClass(Item.class)
                .buildMetadata()
                .buildSessionFactory();

        PurchaseOrder order = new PurchaseOrder(10,  LocalDate.now());
        order.addItem(new Item(1, "ABC", new BigDecimal("84.50")));

        try (Session session = factory.openSession()) {
            Transaction tx = session.beginTransaction();
            session.save(order);
            tx.commit();
        }
    }


    @AfterMethod
    public void shutdown() {
        try (Session session = factory.openSession()) {
            PurchaseOrder obj = this.findOrder(session, 10);
            Transaction tx = session.beginTransaction();
            session.delete(obj);
            tx.commit();
        }

        factory.close();
    }


    @Test
    public void testOrphanDelete() {

        PurchaseOrder clone;
        try (Session session = factory.openSession()) {
            PurchaseOrder obj = this.findOrder(session, 10);
            session.evict(obj);

            clone = this.jsonClone(obj);
            assertEquals(clone.getId(), 10); //passed
        }

        clone.removeItem(clone.getItems().get(0));

        try (Session session = factory.openSession()) {
            Transaction tx = session.beginTransaction();
            assertEquals(clone.getItems().size(), 0); //passed
            session.update(clone);
            tx.commit();
        }

        try (Session session = factory.openSession()) {
            PurchaseOrder obj = this.findOrder(session, 10);
            //AssertionError: expected [0] but found [1]
            assertEquals(obj.getItems().size(), 0);
        }

    }

    private PurchaseOrder findOrder(Session session, int id) {
        Query query = session.createQuery("from PurchaseOrder r "
                + "where r.id=:id");
        query.setParameter("id", id);
        return (PurchaseOrder) query.uniqueResult();
    }


    private PurchaseOrder jsonClone(PurchaseOrder order)  {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

        try {
            String s = mapper.writeValueAsString(order);
            return mapper.readValue(s, PurchaseOrder.class);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }

}

PurchaseOder.java:

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.*;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@JsonIdentityInfo(
        generator=ObjectIdGenerators.PropertyGenerator.class,
        property="id")
@Entity
@Access(AccessType.FIELD)
public class PurchaseOrder {

    @Id
    private int id;
    private LocalDate issueDate;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "order")
    private List<Item> items = new ArrayList<>();

    PurchaseOrder(){}

    public PurchaseOrder(int id, LocalDate issueDate) {
        this.id = id;
        this.issueDate = issueDate;
    }


    final int getId() {
        return id;
    }

    public final LocalDate getIssueDate() {
        return issueDate;
    }


    final List<Item> getItems() {
        return items;
    }

    public void addItem(Item item) {
        item.setOrder(this);
        items.add(item);
    }

    public void removeItem(Item item) {
        item.setOrder(null);
        items.remove(item);
    }
}

Item.java:

import java.math.BigDecimal;
import javax.persistence.*;

@Entity
@Access(AccessType.FIELD)
public class Item {

    @Id
    private int id;
    private String name;
    private BigDecimal price;

    @ManyToOne(fetch = FetchType.EAGER)
    private PurchaseOrder order;

    Item(){}

    Item(int id, String name, BigDecimal price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }


    final int getId() {
        return id;
    }

    final String getName() {
        return name;
    }

    final BigDecimal getPrice() {
        return price;
    }

    final PurchaseOrder getOrder() {
        return order;
    }

    final void setOrder(PurchaseOrder order) {
        this.order = order;
    }

}

hibernate.cfg.xml:

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">org.h2.Driver</property>
        <property name="connection.url">jdbc:h2:D:/test</property>
        <property name="connection.username">sa</property>
        <property name="connection.password">sa</property>

        <property name="hibernate.default_schema">PUBLIC</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.H2Dialect</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <property name="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</property>

        <!-- Drop and re-create the database schema on startup -->
         <property name="hbm2ddl.auto">update</property>

    </session-factory>

</hibernate-configuration>

最后,我的版本号:
Java:8更新181
休眠:5.1.6
杰克逊:2.9.8
H2数据库:1.4.197

更新:问题仅在于孤立孤儿。克隆更新或添加Item子项工作正常。因此,此测试没有失败:

    @Test
    public void testItemAdding() {

        PurchaseOrder clone;
        try (Session session = factory.openSession()) {
            PurchaseOrder obj = this.findOrder(session, 10);
            session.evict(obj);

            clone = this.jsonClone(obj);
        }

        clone.setIssueDate(LocalDate.of(1999, 9, 9));
        clone.addItem(new Item(2, "XYZ", new BigDecimal("182.50")));

        try (Session session = factory.openSession()) {
            Transaction tx = session.beginTransaction();
            session.update(clone);
            tx.commit();
        }

        try (Session session = factory.openSession()) {
            PurchaseOrder obj = this.findOrder(session, 10);
            assertEquals(obj.getIssueDate(), LocalDate.of(1999, 9, 9));
            assertEquals(obj.getItems().size(), 2);
            assertEquals(obj.getItems().get(1).getName(), "XYZ");
        }
   }  

1 个答案:

答案 0 :(得分:0)

对象clone处于detached状态,因此不受jpa管理。您需要先将其保留,以查看通过映射完成的更改。