spring(11)使用对象-关系映射持久化数据
生活随笔
收集整理的這篇文章主要介紹了
spring(11)使用对象-关系映射持久化数据
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【0】README 1)本文部分文字描述轉(zhuǎn)自:“Spring In Action(中/英文版)”,旨在review ?“spring(11)使用對(duì)象-關(guān)系映射持久化數(shù)據(jù)”?的相關(guān)知識(shí);
【2】spring 與 java 持久化API 1)intro:JPA全稱Java Persistence API.JPA通過JDK 5.0注解或XML描述對(duì)象-關(guān)系表的映射關(guān)系,并將運(yùn)行期的實(shí)體對(duì)象持久化到數(shù)據(jù)庫(kù)中。 2)在spring中使用 JPA 第一步是要在 spring應(yīng)用上下文中將 實(shí)體管理器工廠 按照bean 的形式來進(jìn)行配置;(干貨——引入了實(shí)體管理器工廠)
【2.1】配置實(shí)體管理器工廠 ? ? ? ? 1)intro:基于JPA的應(yīng)用程序需要使用 EntityManagerFactory的實(shí)現(xiàn)類來獲取 EntityManager實(shí)例。JPA 定義了 兩種類型的實(shí)體管理器: type1)應(yīng)用程序管理類型:當(dāng)應(yīng)用程序向?qū)嶓w管理器工廠直接請(qǐng)求實(shí)體管理器時(shí),工廠會(huì)創(chuàng)建一個(gè)實(shí)體管理器。在這種模式下,程序要負(fù)責(zé)打開或關(guān)閉實(shí)體管理器并在事務(wù)中對(duì)其進(jìn)行控制。這種方式的實(shí)體管理器適合于不運(yùn)行在 java ee 容器中的獨(dú)立應(yīng)用程序; type2)容器管理類型:實(shí)體管理器由java ee 創(chuàng)建和管理。應(yīng)用程序根本不與實(shí)體管理器工程打交道。相反,實(shí)體管理器直接通過注入或JNDI 來獲取。容器負(fù)責(zé)配置實(shí)體管理器工廠。這種類型的實(shí)體管理器最適用于 java ee 容器,在這種case下 會(huì)希望在 ?persistence.xml 指定的 JPA 配置之外保持一些自己對(duì) JPA的控制;(干貨——容器管理類型的實(shí)體管理器最適用于 java ee 容器) 2)以上兩種實(shí)體管理器實(shí)現(xiàn)了同一個(gè)接口 EntityManager。關(guān)鍵的區(qū)別在于 ?EntityManager 的創(chuàng)建和管理方式。?應(yīng)用程序管理類型的 EntityManager由 EntityManagerFactory 創(chuàng)建的,EntityManagerFactory?是由 PersistenceProvider.createEntityManagerFactory() 方法得到的;而 容器管理類型的 EntityManagerFactorys 是通過 PersistenceProvider.createContainerEntityManagerFactory()方法得到的;(干貨——應(yīng)用程序管理類型和容器管理類型的實(shí)體管理器的關(guān)鍵區(qū)別在于 ?EntityManager 的創(chuàng)建和管理方式)
3)這兩種實(shí)體管理器工廠分別由對(duì)應(yīng)的 spring 工廠 bean 創(chuàng)建: type1)LocalEntityManagerFactoryBean: 生成應(yīng)用程序管理類型的 EntityManagerFactory; type2)LocalContainerEntityManagerFactoryBean: 生成容器管理類型的 EntityManagerFactory;
Attention)應(yīng)用程序管理類型和容器管理類型的實(shí)體管理器工廠間唯一值得關(guān)注的區(qū)別是:在 spring 應(yīng)用上下文中如何進(jìn)行配置;
【2.1.1】配置應(yīng)用程序管理類型的JPA 1)intro:對(duì)于應(yīng)用程序管理類型的實(shí)體管理器工廠來說,它絕大部分配置信息來源于一個(gè) 名為 persistence.xml 的配置文件,這個(gè)文件必須位于 類路徑下的 META-INF 目錄下; 2)下面是一個(gè)典型的 persistence.xml 文件: <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence"version="1.0"><persistence-unit name="spitterPU"><class>com.habuma.spittr.domain.Spitter</class><class>com.habuma.spittr.domain.Spittle</class><properties><property name="toplink.jdbc.driver" value="org.hsqldb.jdbcDriver" /><property name="toplink.jdbc.url"value="jdbc:hsqldb:hsql://localhost/spitter/spitter" /><property name="toplink.jdbc.user" value="sa" /><property name="toplink.jdbc.password" value="" /></properties></persistence-unit> </persistence> 3)可以通過如下的 @Bean注解方法在spring中聲明 LocalEntityManagerFactoryBean:
【2.1.2】使用容器管理類型的JPA 1)intro:容器管理的JPA 采取了一個(gè)不同的方式。當(dāng)運(yùn)行在容器中時(shí),可以使用容器提供的信息來生成 EntityManagerFactory; 2)你可以將 數(shù)據(jù)源信息配置在 spring應(yīng)用上下文中,而不是在 persistence.xml 中了。如下的@Bean 注解方法聲明了在 spring中如何使用 LocalContainerEntityManagerFactoryBean 來配置容器管理類型的 JPA:
對(duì)以上代碼的分析(Analysis):? A1)這里,我們使用了 spring配置的數(shù)據(jù)源來設(shè)置 datasource屬性。任何 javax.sql.DataSource 的實(shí)現(xiàn)都是可以的; A2)jpaVendorApapter屬性:用于指明所使用的是哪一個(gè)廠商的JPA 實(shí)現(xiàn),spring提供了多個(gè) JPA 廠商適配器: adapter1)EclipseLinkJpaVendorAdapter adapter2)HibernateJpaVendorAdapter adapter3)OpenJpaVendorAdapter adapter4)TopLinkJpaVendorAdapter (deprecated in Spring 3.1)
【2.1.3】從JNDI 獲取實(shí)體管理器工廠 1)intro:如果將spring應(yīng)用程序部署在應(yīng)用server中, 則 EntityManagerFactory 可能已經(jīng)被創(chuàng)建好了,并且位于 JNDI 中等待查詢使用; 2)在這種case下,可以使用 spring jee 命名空間下的 <jee: jndi-lookup>元素來獲取對(duì) EntityManagerFactory 的引用: <jee:jndi-lookup id="emf" jndi-name="persistence/spitterPU" /> 3)可以使用 java Config 來獲取?EntityManagerFactory?: @Bean public JndiObjectFactoryBean entityManagerFactory() {}JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();jndiObjectFB.setJndiName("jdbc/SpittrDS");return jndiObjectFB; } 對(duì)以上代碼的分析(Analysis): 因?yàn)樯鲜龇椒ǚ祷氐?JndiObjectFactoryBean?是 FactoryBean 接口的實(shí)現(xiàn),它能夠創(chuàng)建 EntityManagerFactory; Attention)自此,我們就得到了?EntityManagerFactory 對(duì)象了;
【2.2】編寫基于 JPA 的 Repository 1)intro:spring 對(duì) JPA 集成也提供了JpaTemplate 模板以及對(duì)應(yīng)的支持類 JpaDaoSupport; 2)鑒于純粹的JPA 方式遠(yuǎn)遠(yuǎn)勝于基于模板的 JPA,所以在本節(jié)中我們將會(huì)重點(diǎn)關(guān)注如何構(gòu)建不依賴spring 的 JPA Repository。如下程序中的 JpaSpitterRepository 展現(xiàn)了 如何開發(fā)不使用 spring JpaTemplate 的 JPA Repository; @Repository @Transactional public class JpaSpitterRepository implements SpitterRepository {@PersistenceUnitprivate EntityManagerFactory emf;public void addSpitter(Spitter spitter) {emf.createEntityManager().persist(spitter);}public Spitter getSpitterById(long id) {return emf.createEntityManager().find(Spitter.class, id);}public void saveSpitter(Spitter spitter) {emf.createEntityManager().merge(spitter);}... } 對(duì)以上代碼的分析(Analysis):
A1)@PersistenceUnit注解:spring會(huì)將 EntityManagerFactory 注入到 Repository中;(干貨——注解@PersistenceUnit的作用) A2)有了 EntityManagerFactory之后,JpaSpitterRepository 的方法就能夠使用它來創(chuàng)建 EntityManager了,然后 EntityManager 可以針對(duì)數(shù)據(jù)庫(kù)執(zhí)行操作; A3)JpaSpitterRepository?唯一的問題:在于每個(gè)方法都會(huì)調(diào)用createEntityManager()方法;
3)下面的程序展現(xiàn)了 如何借助 @PersistenceContext注解為 JpaSpitterRepository 設(shè)置 EntityManager; @Repository @Transactional public class JpaSpitterRepository implements SpitterRepository {@PersistenceContextprivate EntityManager em;public void addSpitter(Spitter spitter) {em.persist(spitter);}public Spitter getSpitterById(long id) {return em.find(Spitter.class, id);}public void saveSpitter(Spitter spitter) {em.merge(spitter);}... } 對(duì)以上代碼的分析(Analysis): A0)@Transactional注解的作用: 表明這個(gè) Repository中的持久化方法是在事務(wù)上下文中執(zhí)行的;(干貨——@Transactional注解的作用) A1)由于沒有使用模板類來處理異常,所以我們需要為 Repository添加 @Repository 注解,這樣PersistenceAnnotationBeanPostProcessor 就會(huì)知道要將這個(gè)bean 產(chǎn)生 的異常轉(zhuǎn)換為 spring 的統(tǒng)一數(shù)據(jù)訪問異常; A2)在上面的JpaSpitterRepository?中,直接為其設(shè)置了?EntityManager?;這樣的話,每個(gè)方法中就沒有必須再通過 EntityManagerFactory 創(chuàng)建 EntityManager了;(盡管這種方式非常便利,但你可能會(huì)擔(dān)心注入的EntityManager?會(huì)有?線程安全問題) A3)這里的真相是:@PersistenceContext 并不會(huì)真正注入?EntityManager;它沒有將真正的?EntityManager 設(shè)置給 Repository,而是給了它一個(gè)?EntityManager 的代理;真正的EntityManager 是與當(dāng)前事務(wù)相關(guān)聯(lián)的那一個(gè),如果不存在這樣的?EntityManager 的話,就會(huì)創(chuàng)建一個(gè)新的。這樣的話,我們就能始終以 線程安全的方式提供實(shí)體管理器了;
4)需要了解 @PersistenceUnit and @PersistenceContext 并不是spring 的注解,而是 JPA規(guī)范提供的。 4.1)為了讓spring理解 這些注解,并注入 EntityManagerFactory or EntityManager,我們必須要配置 spring 的 PersistenceAnnotationBeanPostProcessor ; 4.2)如果你已經(jīng)使用了 <context:annotation-config> or <context:component-scan>,那么你不必?fù)?dān)心了,因?yàn)檫@些配置元素會(huì)自動(dòng)注冊(cè) PersistenceAnnotationBeanPostProcessor bean;否則的話,我們就要顯示地注入這個(gè) bean; @Bean public PersistenceAnnotationBeanPostProcessor paPostProcessor() {return new PersistenceAnnotationBeanPostProcessor(); }
【2】spring 與 java 持久化API 1)intro:JPA全稱Java Persistence API.JPA通過JDK 5.0注解或XML描述對(duì)象-關(guān)系表的映射關(guān)系,并將運(yùn)行期的實(shí)體對(duì)象持久化到數(shù)據(jù)庫(kù)中。 2)在spring中使用 JPA 第一步是要在 spring應(yīng)用上下文中將 實(shí)體管理器工廠 按照bean 的形式來進(jìn)行配置;(干貨——引入了實(shí)體管理器工廠)
【2.1】配置實(shí)體管理器工廠 ? ? ? ? 1)intro:基于JPA的應(yīng)用程序需要使用 EntityManagerFactory的實(shí)現(xiàn)類來獲取 EntityManager實(shí)例。JPA 定義了 兩種類型的實(shí)體管理器: type1)應(yīng)用程序管理類型:當(dāng)應(yīng)用程序向?qū)嶓w管理器工廠直接請(qǐng)求實(shí)體管理器時(shí),工廠會(huì)創(chuàng)建一個(gè)實(shí)體管理器。在這種模式下,程序要負(fù)責(zé)打開或關(guān)閉實(shí)體管理器并在事務(wù)中對(duì)其進(jìn)行控制。這種方式的實(shí)體管理器適合于不運(yùn)行在 java ee 容器中的獨(dú)立應(yīng)用程序; type2)容器管理類型:實(shí)體管理器由java ee 創(chuàng)建和管理。應(yīng)用程序根本不與實(shí)體管理器工程打交道。相反,實(shí)體管理器直接通過注入或JNDI 來獲取。容器負(fù)責(zé)配置實(shí)體管理器工廠。這種類型的實(shí)體管理器最適用于 java ee 容器,在這種case下 會(huì)希望在 ?persistence.xml 指定的 JPA 配置之外保持一些自己對(duì) JPA的控制;(干貨——容器管理類型的實(shí)體管理器最適用于 java ee 容器) 2)以上兩種實(shí)體管理器實(shí)現(xiàn)了同一個(gè)接口 EntityManager。關(guān)鍵的區(qū)別在于 ?EntityManager 的創(chuàng)建和管理方式。?應(yīng)用程序管理類型的 EntityManager由 EntityManagerFactory 創(chuàng)建的,EntityManagerFactory?是由 PersistenceProvider.createEntityManagerFactory() 方法得到的;而 容器管理類型的 EntityManagerFactorys 是通過 PersistenceProvider.createContainerEntityManagerFactory()方法得到的;(干貨——應(yīng)用程序管理類型和容器管理類型的實(shí)體管理器的關(guān)鍵區(qū)別在于 ?EntityManager 的創(chuàng)建和管理方式)
3)這兩種實(shí)體管理器工廠分別由對(duì)應(yīng)的 spring 工廠 bean 創(chuàng)建: type1)LocalEntityManagerFactoryBean: 生成應(yīng)用程序管理類型的 EntityManagerFactory; type2)LocalContainerEntityManagerFactoryBean: 生成容器管理類型的 EntityManagerFactory;
Attention)應(yīng)用程序管理類型和容器管理類型的實(shí)體管理器工廠間唯一值得關(guān)注的區(qū)別是:在 spring 應(yīng)用上下文中如何進(jìn)行配置;
【2.1.1】配置應(yīng)用程序管理類型的JPA 1)intro:對(duì)于應(yīng)用程序管理類型的實(shí)體管理器工廠來說,它絕大部分配置信息來源于一個(gè) 名為 persistence.xml 的配置文件,這個(gè)文件必須位于 類路徑下的 META-INF 目錄下; 2)下面是一個(gè)典型的 persistence.xml 文件: <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence"version="1.0"><persistence-unit name="spitterPU"><class>com.habuma.spittr.domain.Spitter</class><class>com.habuma.spittr.domain.Spittle</class><properties><property name="toplink.jdbc.driver" value="org.hsqldb.jdbcDriver" /><property name="toplink.jdbc.url"value="jdbc:hsqldb:hsql://localhost/spitter/spitter" /><property name="toplink.jdbc.user" value="sa" /><property name="toplink.jdbc.password" value="" /></properties></persistence-unit> </persistence> 3)可以通過如下的 @Bean注解方法在spring中聲明 LocalEntityManagerFactoryBean:
【2.1.2】使用容器管理類型的JPA 1)intro:容器管理的JPA 采取了一個(gè)不同的方式。當(dāng)運(yùn)行在容器中時(shí),可以使用容器提供的信息來生成 EntityManagerFactory; 2)你可以將 數(shù)據(jù)源信息配置在 spring應(yīng)用上下文中,而不是在 persistence.xml 中了。如下的@Bean 注解方法聲明了在 spring中如何使用 LocalContainerEntityManagerFactoryBean 來配置容器管理類型的 JPA:
對(duì)以上代碼的分析(Analysis):? A1)這里,我們使用了 spring配置的數(shù)據(jù)源來設(shè)置 datasource屬性。任何 javax.sql.DataSource 的實(shí)現(xiàn)都是可以的; A2)jpaVendorApapter屬性:用于指明所使用的是哪一個(gè)廠商的JPA 實(shí)現(xiàn),spring提供了多個(gè) JPA 廠商適配器: adapter1)EclipseLinkJpaVendorAdapter adapter2)HibernateJpaVendorAdapter adapter3)OpenJpaVendorAdapter adapter4)TopLinkJpaVendorAdapter (deprecated in Spring 3.1)
【2.1.3】從JNDI 獲取實(shí)體管理器工廠 1)intro:如果將spring應(yīng)用程序部署在應(yīng)用server中, 則 EntityManagerFactory 可能已經(jīng)被創(chuàng)建好了,并且位于 JNDI 中等待查詢使用; 2)在這種case下,可以使用 spring jee 命名空間下的 <jee: jndi-lookup>元素來獲取對(duì) EntityManagerFactory 的引用: <jee:jndi-lookup id="emf" jndi-name="persistence/spitterPU" /> 3)可以使用 java Config 來獲取?EntityManagerFactory?: @Bean public JndiObjectFactoryBean entityManagerFactory() {}JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();jndiObjectFB.setJndiName("jdbc/SpittrDS");return jndiObjectFB; } 對(duì)以上代碼的分析(Analysis): 因?yàn)樯鲜龇椒ǚ祷氐?JndiObjectFactoryBean?是 FactoryBean 接口的實(shí)現(xiàn),它能夠創(chuàng)建 EntityManagerFactory; Attention)自此,我們就得到了?EntityManagerFactory 對(duì)象了;
【2.2】編寫基于 JPA 的 Repository 1)intro:spring 對(duì) JPA 集成也提供了JpaTemplate 模板以及對(duì)應(yīng)的支持類 JpaDaoSupport; 2)鑒于純粹的JPA 方式遠(yuǎn)遠(yuǎn)勝于基于模板的 JPA,所以在本節(jié)中我們將會(huì)重點(diǎn)關(guān)注如何構(gòu)建不依賴spring 的 JPA Repository。如下程序中的 JpaSpitterRepository 展現(xiàn)了 如何開發(fā)不使用 spring JpaTemplate 的 JPA Repository; @Repository @Transactional public class JpaSpitterRepository implements SpitterRepository {@PersistenceUnitprivate EntityManagerFactory emf;public void addSpitter(Spitter spitter) {emf.createEntityManager().persist(spitter);}public Spitter getSpitterById(long id) {return emf.createEntityManager().find(Spitter.class, id);}public void saveSpitter(Spitter spitter) {emf.createEntityManager().merge(spitter);}... } 對(duì)以上代碼的分析(Analysis):
A1)@PersistenceUnit注解:spring會(huì)將 EntityManagerFactory 注入到 Repository中;(干貨——注解@PersistenceUnit的作用) A2)有了 EntityManagerFactory之后,JpaSpitterRepository 的方法就能夠使用它來創(chuàng)建 EntityManager了,然后 EntityManager 可以針對(duì)數(shù)據(jù)庫(kù)執(zhí)行操作; A3)JpaSpitterRepository?唯一的問題:在于每個(gè)方法都會(huì)調(diào)用createEntityManager()方法;
3)下面的程序展現(xiàn)了 如何借助 @PersistenceContext注解為 JpaSpitterRepository 設(shè)置 EntityManager; @Repository @Transactional public class JpaSpitterRepository implements SpitterRepository {@PersistenceContextprivate EntityManager em;public void addSpitter(Spitter spitter) {em.persist(spitter);}public Spitter getSpitterById(long id) {return em.find(Spitter.class, id);}public void saveSpitter(Spitter spitter) {em.merge(spitter);}... } 對(duì)以上代碼的分析(Analysis): A0)@Transactional注解的作用: 表明這個(gè) Repository中的持久化方法是在事務(wù)上下文中執(zhí)行的;(干貨——@Transactional注解的作用) A1)由于沒有使用模板類來處理異常,所以我們需要為 Repository添加 @Repository 注解,這樣PersistenceAnnotationBeanPostProcessor 就會(huì)知道要將這個(gè)bean 產(chǎn)生 的異常轉(zhuǎn)換為 spring 的統(tǒng)一數(shù)據(jù)訪問異常; A2)在上面的JpaSpitterRepository?中,直接為其設(shè)置了?EntityManager?;這樣的話,每個(gè)方法中就沒有必須再通過 EntityManagerFactory 創(chuàng)建 EntityManager了;(盡管這種方式非常便利,但你可能會(huì)擔(dān)心注入的EntityManager?會(huì)有?線程安全問題) A3)這里的真相是:@PersistenceContext 并不會(huì)真正注入?EntityManager;它沒有將真正的?EntityManager 設(shè)置給 Repository,而是給了它一個(gè)?EntityManager 的代理;真正的EntityManager 是與當(dāng)前事務(wù)相關(guān)聯(lián)的那一個(gè),如果不存在這樣的?EntityManager 的話,就會(huì)創(chuàng)建一個(gè)新的。這樣的話,我們就能始終以 線程安全的方式提供實(shí)體管理器了;
4)需要了解 @PersistenceUnit and @PersistenceContext 并不是spring 的注解,而是 JPA規(guī)范提供的。 4.1)為了讓spring理解 這些注解,并注入 EntityManagerFactory or EntityManager,我們必須要配置 spring 的 PersistenceAnnotationBeanPostProcessor ; 4.2)如果你已經(jīng)使用了 <context:annotation-config> or <context:component-scan>,那么你不必?fù)?dān)心了,因?yàn)檫@些配置元素會(huì)自動(dòng)注冊(cè) PersistenceAnnotationBeanPostProcessor bean;否則的話,我們就要顯示地注入這個(gè) bean; @Bean public PersistenceAnnotationBeanPostProcessor paPostProcessor() {return new PersistenceAnnotationBeanPostProcessor(); }
總結(jié)
以上是生活随笔為你收集整理的spring(11)使用对象-关系映射持久化数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ps怎么让图片局部透明(ps怎么让图片局
- 下一篇: ps怎么做网页效果图(ps怎么做网页效果