首先創建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;
大概就這麼多了,研究了差不多兩天。