@OneToOne 註解學習記錄

首先創建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;

大概就這麼多了,研究了差不多兩天。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章