我在使用休眠持久化克隆对象时遇到问题。删除其嵌套子级后,该记录并未从数据库中删除(我已将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");
}
}
答案 0 :(得分:0)
对象clone
处于detached
状态,因此不受jpa管理。您需要先将其保留,以查看通过映射完成的更改。