首先创建2个实体类,Person , Passport; 人员和护照是一对一的关系;
首先做单向关联测试,只在Person 中 关联 Passport
此时的表单结构是:
测试一: OneToOne 采用默认选项
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne
private Passport passport;
...
}
@Entity
public class Passport {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String number;
//...
}
然后跑测试代码,添加一个人员,并设置他的护照,在没有保存Passport 的情况下是无法保存Person 的。
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
PersonDao personDao;
@Autowired
PassportDao passportDao;
@Test
public void addTest()
{
Person p = new Person();
p.setName("张三");
Passport passport = new Passport();
passport.setNumber(UUID.randomUUID().toString());
p.setPassport(passport);
personDao.save(p); // 在OneToOne 关联,没有设置cascade 的情况下;这里无法保存的,必 须先保存 passport,在来保存Person;
}
}
测试二: OneToOne 采用cascade=CascadeType.All 的情况
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade=CascadeType.ALL)
private Passport passport;
}
在跑上面同样的测试代码,测试通过,数据库生成两条记录,并正确关联。
测试三: 在测试二的结果上,删除Person 中张三的记录, 张三的Passport 也跟着删除
@Test
public void delTest()
{
Person p = personDao.findById(1);
personDao.delete(p); // person 关联的 passport 记录也从数据库删除了
}
测试四: 在测试二的结果上,删除passport,即删除张三的护照,不删除张三:结果是,护照被删除,张三信息里面的护照外键还是存在:passport_id = 3; 但 passport 的信息其实已经被删除了。
在开始下一步测试前,先来学习下CascadeType的解释:
ALL
级联所有实体状态转换
PERSIST
级联实体持久化操作:当父实体被持久化时,会连同持久化子实体
啥意思呢? 在我们上面的例子中就是,如果Person 保存时,里面设置的 passport 还没保存的情况下, 会自动保存passport;
MERGE
:
级联实体合并操作。获取子实体时,会连同获取级联的父实体。这个级联只能合并数据库已存在的实体;这种情况一般只能用在双向关联
啥意思呢? 就是查出passport的数据,能把passport 的拥有者一并查出,并且在修改 拥有者person的字段时,passport 也跟着变化。
REMOVE
级联实体删除操作。当父实体被删除时,会连同删除子实体;
REFRESH
级联实体刷新操作。
DETACH
级联实体分离操作。
测试五:
在passport 类中也加入 OneToOne 注解
@Entity
public class Passport {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@OneToOne(cascade={CascadeType.REMOVE,CascadeType.PERSIST})
private Person owner;
private String number;
//...
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade= {CascadeType.REMOVE,CascadeType.PERSIST})
private Passport passport;
}
此时生成的数据库为:
passport表多了一个owner_id 的外键字段,并且在持久化测试中(上面的addTest),没有自动关联到person中去
在OneToOne 的注解下,我们把张三的护照,再添加到李四下面是否可以呢? 应该是不可以的,我们测试一下
{
Person p = new Person();
p.setName("张三");
Passport passport = new Passport();
passport.setNumber(UUID.randomUUID().toString());
p.setPassport(passport);
personDao.save(p);
//把张三的护照给李四
Person p2 = new Person();
p2.setName("李四");
p2.setPassport(passport);
//给护照指定成李四的
//passport.setOwner(p2);
personDao.save(p2);
}
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.example.demo.entity.Passport; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.demo.entity.Passport
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:317)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy99.save(Unknown Source)
at com.example.demo.DemoApplicationTests.测试联动保存cascade为PERSIST(DemoApplicationTests.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.demo.entity.Passport
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:824)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:791)
at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:471)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:396)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:197)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:130)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:455)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:269)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:200)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:131)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:800)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:785)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:308)
at com.sun.proxy.$Proxy96.persist(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:489)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
... 41 more
测试六: mappedBy
只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;mappedBy标签一定是定义在被拥有方的,他指向拥有方即(定义在不维护关系的那一方);mappedBy的含义,应该理解为,拥有方能够自动维护跟被拥有方的关系,当然,如果从被拥有方,通过手工强行来维护拥有方的关系也是可以做到的;mappedBy跟joinColumn/JoinTable总是处于互斥的一方,
在passport上加上mappedy来测试
@Entity
public class Passport {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@OneToOne(mappedBy="passport",cascade={CascadeType.ALL})
private Person owner;
private String number;
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade= {CascadeType.PERSIST})
private Passport passport;
}
此时,生成的数据库表格如下,passport 表单中已经不存在person的外键了。那如果不存在person的外键,那我删除张三的passport时,person表中的张三数据会怎么样呢?
测试结果:
(casadeType.ALL 的情况下): person 也会同时被删除,日志中可以看到两条删除语句:
Hibernate: select passport0_.id as id1_0_0_, passport0_.number as number2_0_0_, person1_.id as id1_1_1_, person1_.name as name2_1_1_, person1_.passport_id as passport3_1_1_ from passport passport0_ left outer join person person1_ on passport0_.id=person1_.passport_id where passport0_.id=?
Hibernate: select passport0_.id as id1_0_1_, passport0_.number as number2_0_1_, person1_.id as id1_1_0_, person1_.name as name2_1_0_, person1_.passport_id as passport3_1_0_ from passport passport0_ left outer join person person1_ on passport0_.id=person1_.passport_id where passport0_.id=?
Hibernate: delete from person where id=?
Hibernate: delete from passport where id=?
把Person 的OneToOne注解改成
@OneToOne(cascade= {CascadeType.PERSIST})
private Passport passport;
再做删除passport 表单的测试:
@Test
public void delPassportTest()
{
Passport p = passportDao.findById(1);
passportDao.delete(p);
}
此时,测试会异常,无法删除
org.springframework.dao.InvalidDataAccessApiUsageException: The entity must not be null!; nested exception is java.lang.IllegalArgumentException: The entity must not be null!
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:367)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy99.delete(Unknown Source)
at com.example.demo.DemoApplicationTests.delPassportTest(DemoApplicationTests.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: java.lang.IllegalArgumentException: The entity must not be null!
at org.springframework.util.Assert.notNull(Assert.java:198)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.delete(SimpleJpaRepository.java:160)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
... 41 more
把Person 的OneToOne注解改成
@OneToOne(cascade= {CascadeType.PERSIST,CascadeType.REMOVE})
private Passport passport;
再做删除passport 表单的测试,相关联的person信息也被删除了。
那passport 的 casadeType 改成persist 呢,还能删除吗?测试结果是不能的。
@Entity
public class Passport {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@OneToOne(mappedBy="passport",cascade={CascadeType.PERSIST})
private Person owner;
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne (cascade= {CascadeType.PERSIST})
private Passport passport;
}
// 此时,删除护照表的数据,是无法删除的,测试不会报错,但无法删除数据。
@Test
public void delPassportTest()
{
Passport p = passportDao.findById(5);
passportDao.delete(p);
}
那要单独删除张三的护照,应该怎么做呢?
首先,在Peson类里面允许护照为空值 optional=true
@OneToOne (cascade= {CascadeType.PERSIST},optional=true)
private Passport passport;
然后删除的方式和顺序改一下,因为关系是由person表来维护的,所以先要对person表做操作,在person表里找到张三,把张三的护照设置为空,并保存;然后去删除原来的护照。
public void delPassportTest(int id)
{
Passport p = passportDao.findById(id);
Person perosn = p.getOwner();
perosn.setPassport(null);
personDao.save(perosn);
passportDao.deleteById(p.getId());
}
总结:
1,有设置 mappedBy 标注字段的类就是被维护表,如上面的passport。
2, cascade 的值是对它设置的属性对象 进行的级联操作; 如Person 类中的passport设置为 REMOVE 的话,删除peson对象, passport 对象也会删除;
@OneToOne (cascade= {CascadeType.REMOVE},optional=true)
private Passport passport;
3, optional=true 允许对象为空,默认是fasle
4,fetch = LAYZ, EGER, 懒加载和急加载, 待具体研究,默认是EGER;
大概就这么多了,研究了差不多两天。