1. 程式人生 > >spring的使用-jdbc、事務

spring的使用-jdbc、事務

spring對資料庫的操作:jdbcTemplate操作及事務管理

1.jdbcTemplate操作資料庫:

      1. 匯入jar包(3個)

            spring-jdbc-4.2.4.RELEASE.jar

            spring-tx-4.2.4.RELEASE.jar

            以及jdbc驅動jar包

      2. 建立連線池

            DriverManagerDataSource dataSource = new DriverManagerDataSource();          //為spring內建連線池

      3. 設定連線池引數

            dataSource.setDriverClassName("com.mysql.jdbc.Driver");

            dataSource.setUrl("jdbc:mysql:///springtest");

            dataSource.setUsername("root");

            dataSource.setPassword("abc");

      4. 建立JdbcTemplate物件設定引數

            JdbcTemplate jdbcTemplate=new JdbcTemplate();

            jdbcTemplate.setDataSource(dataSource);

      6. 執行sql語句

            jdbcTemplate.execute("update t_user set name='張三' where id=1");//execute方法可以執行任意sql

           

      總結:jdbcTemplate類似於jdbcUtils中的queryrunner物件,需要傳入一個連線池,可以執行sql語句

 

2.使用spring容器簡化上述操作

      將連線池物件DriverManagerDataSource,和temple物件jdbcTemplate都交給spring管理

            1. applicationContext.xml配置:

                  spring內建的連線池DriverManagerDataSource,如下:      //如果要使用c3p0連線池:先匯入兩個c3p0的兩個jar包,在下面的class中傳入ComboPooledDataSource全類名,並採用set方法設定響應屬性即可

                  <bean id="driverManagerDateSource"    class="org.springframework.jdbc.datasource.DriverManagerDataSource">           

                          <property name="driverClassName" value="com.mysql.jdbc.Driver" /> //採用set方法注入屬性,DriverManagerDataSource有對應的set方法

                          <property name="url" value="jdbc:mysql:///springtest" />

                          <property name="username" value="root" />

                          <property name="password" value="abc"></property>

                  </bean>

 

                  配置jdbcTemplate

                  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

                        <property name="dataSource" ref="driverManagerDateSource"></property>      //set方法注入屬性

                  </bean>

           

            2. 簡化後操作:

                  @RunWith(SpringJUnit4ClassRunner.class)

                  @ContextConfiguration(locations = "classpath:applicationContext.xml")

                  public class JdbcTemplateTest2 {

                       @Autowired

                       private JdbcTemplate jdbcTemplate;    //從spring容器中獲取物件

 

                       @Test

                       public void test1() {

                       jdbcTemplate.execute("update t_user set sex='女'");   //執行sql語句

                       }

                  }

                      

3.通過JdbcDaoSurport 獲取jdbctemplate進一步簡化操作           //只需要向jdbcDaoSurport中注入連線池即可

      JdbcDaoSurport類中聲明瞭jdbctemplate和setJdbcTemplate(),和連線池setDataSource()方法。

     

      public class AccountDAOImpl extends JdbcDaoSupport implements IAccountDAO {       //讓dao繼承JdbcDaoSuppory,需要注入連線池

 

            @Override

            public void accountOut(String outname, double money) {

                  this.getJdbcTemplate().update("update account set money=money-? where name=?", money, outname);       //注入連線池後,直接呼叫getJdbcTemplate();的方法獲取JdbcTemplate物件

            }

      }

     

      如果使用註解方式,注入dataSource的方式:

            @Component

            public class AccountDAOImpl extends JdbcDaoSupport implements IAccountDAO {

                 

                  @Autowired

                  public void setDs(DataSource ds){ //注入時,會在spring容器中查詢DataSource的實現類物件,並當作引數傳入

                       super.setDataSource(ds);   //呼叫父類的方法實現連線池注入,注入連線池後,父類會自動建立jdbcTemplate物件

                  }

                  ...

            }

           

4.Jdbctemplate應用--crud操作         

      增刪改:update("");           //傳入sql語句,與queryrunner一樣

            jdbcTemplate.update("update t_user set name=? where id=?", "tom", 1);  //sql語句還可以是insert into 或delete from ...

      查:queryForObject("",class);

 

 

5.引入外部屬性檔案properties

      1. 建立properties屬性檔案如:db.properties

     

      2. 在applicationContext.xml中引入properties檔案:

            <context:property-placeholder location="classpath:db.properties" />

           

      3. 在配置檔案或註解中均可使用${name}方式來獲取屬性:

            <!-- 建立c3p0連線濱 -->

            <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

                  <property name="driverClass" value="${jdbc.driverClass}" />       //獲取properties外部檔案中的屬性並注入

                  <property name="jdbcUrl" value="${jdbc.url}" />

                  <property name="user" value="${jdbc.username}" />      //jdbc.username如果改為usesrname會出錯??

                  <property name="password" value="${jdbc.password}" />

            </bean>

     

     

6.事務管理機制(三個介面):

      1. PlatformTransactionManager    --->平臺事務管理器,在不同的持久化層,事務程式碼不一樣

            常用的實現類:

                  DataSourceTransactionManager   主要針對於JdbcTemplate開發  MyBatis開發

                  HibernateTransactionManasger    主要針對於Hibernate開發

                  JpaTransactionManager                主要針對於JPA開發

           

            介面中的三個方法:

                  getTransaction();    //開啟事務

                  commit();         //提交事務

                  rollback();        //回滾事務

 

      2. TransactionDefinition     --->定義事務的一些相關資訊 例如 隔離 傳播 超時 只讀

            隔離:

                  ISOLATION_DEFUALT              它使用後端資料庫的預設隔離級別(spring中選項)

                  ISOLATION_READ_UNCOMMITTED     不能解決問題,會發生髒讀 不可重複讀 虛讀

                  ISOLATION_READ_COMMITTED    可以解決髒讀 會產生不可重複讀與虛讀。

                  ISOLATION_REPEATABLE_READ     可以解決髒讀,不可重複讀 解決不了虛讀

                  ISOLATION_SERIALIZABLE             序列化,可以解決所有問題

 

            傳播:  //解決的是兩個被事務管理的方法互相呼叫問題(A呼叫B)。它與資料庫沒關係,是程式內部維護的問題

                  PROPAGATION_REQUIRED       預設值 兩個操作處於同一個事務,如果之前沒有事務,新建一個事務。 兩個事務要麼同時成功,要麼同時失敗

                  PROPAGATION_REQUIRES_NEW     B會新建一個事務,兩個操作處於不同的事務,A和B互不影響

                  PROPAGATION_NESTED                它是一種巢狀事務,它是使用SavePoint來實現的。A可以影響B,但是B不能影響A。事務回滾時可以回滾到指定的savepoint, 注意:它只對DataSourceTransactionManager有作用

                                                    

                  PROPAGATION_SUPPORTS 支援當前事務,如果不存在,就不使用事務

                  PROPAGATION_MANDATORY 支援當前事務,如果不存在,丟擲異常

                  PROPAGATION_NOT_SUPPORTED 以非事務執行,如果有事務存在,掛起當前事務

                  PROPAGATION_NEVER 以非事務執行,如果有事務存在,丟擲異常

 

            超時:int TIMEOUT_DEFAULT = -1;     預設值是-1 它使用的是資料庫預設的超時時間

            只讀:boolean isReadOnly();         它的值有兩個true/false,如果選擇true一般是在select操作時

           

      3. TransactionStatus     --->定義了事務狀態資訊,在事務執行過程中,得到某個時間點的狀態

           

     

7.spring事務管理-事務管理兩種方式

      1.   編碼方案 不建議使用,它具有侵入性。在原有的業務程式碼基礎上去新增事務管理程式碼

     

            @Autowired

            private PlatformTransactionManager transactionManager;      //注入方式獲取事務管理器物件

     

            @Override

            public void account(String outname, String inname, double money)  {

                  DefaultTransactionDefinition def = new DefaultTransactionDefinition(); //建立TransactionDefinition實現類物件

                def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);      //設定事務相關資訊引數

            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);     

                  def.setReadOnly(false);

                  def.setTimeout(TransactionDefinition.TIMEOUT_DEFAULT);

                 

                  TransactionStatus status = transactionManager.getTransaction(def); //開啟事務,需要傳入事務定義TransactionDefinition物件

                                                                                                                     //此處得到的是一個事務狀態的物件

                  try {

                       // 邏輯程式碼

                       // 從outname轉出money

                       accountDao.accountOut(outname, money);

                       int a = 10 / 0; // 一定會丟擲異常

                       // 向inname轉入money

                       accountDao.accountIn(inname, money);

                      

                  } catch (Exception e) {

                       //事務回滾

                       transactionManager.rollback(status);   //傳入事務狀態物件記錄狀態資訊

                  } finally {

                       //提交事務

                       transactionManager.commit(status);

                  }

            }

     

            總結:三個介面的物件相互配合完成事務控制,PlatformTransactionManager物件負責開啟、關閉、回滾事務,

                  TransactionDefinition,負責封裝事務相關資訊,      TransactionStatus負責記錄事務狀態資訊

     

      2.   宣告式事務控制,基於AOP對目標進行代理,新增around環繞通知。      //優點:不具有侵入性,不需要修改原來的業務程式碼

            a.xml 配置檔案實現

                  第一步:在applicationContext.xml檔案中新增aop與tx的名稱空間

                  第二步:配置事務管理器

                       <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

                             <property name="dataSource" ref="c3p0DataSource"/>        //需要注入連線池

                       </bean>

 

                  第三步:配置通知   //Spring為我們提供了一個TransactionInterceptor來完成增強, 對於這個增強,可以使用spring為我們提供的一個標籤<tx:advice>來完成操作

                       <tx:advice id="txAdvice" transaction-manager="transactionManager">

                             <tx:attributes>

                                   <tx:method name="account" />

                             </tx:attributes>

                       </tx:advice>

                      

                       <tx:method>中的屬性 //相當於定義事務相關資訊

                       name:必須的,對哪些方法進行事務控制

                       isolation 可選 設定事務隔離級別 預設是DEFAULT

                       propagation:可選 設定事務傳播 預設值 REQUIRED

                       timeout 可選 超時時間 預設值-1

                       read-only 可選 預設值是false 如果不是隻讀,它可以對insert update delete操作,如果是隻讀不可以。

                       rollback-for 可選 可以設定一個異常,如果產生這個異常,觸發事務回滾

                       no-rolback-for 可選 可以設定一個異常,如果產生這個異常,不會觸發事務回滾

                 

                  第四步:配置切面 //使用的是傳統的spring的advice,需要使用<aop:advisor>標籤配置切面

                       <aop:config>

                             <aop:pointcut

                                   expression="execution(* cn.itheima.service.IAccountService.account(..))" id="txPointcut"/>

                             <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

                       </aop:config>

 

                      

           

            b.註解實現

                  1. 配置事務管理器       //需要提前配置連線池

                       <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

                             <property name="dataSource" ref="c3p0DataSource"/>        //必須注入dataSource

                       </bean>

                  2. 開啟註解事務

                       <tx:annotation-driven transaction-manager="transactionManager"/>            //必須傳入事務管理器

 

                  3. 方法或類上使用註解:

                  @Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)      //可以在括號中加入事務資訊,也可省略

                       @Override

                       public void account(String outname, String inname, double money) {

                             // 從outname轉出money

                             accountDao.accountOut(outname, money);

                             int a = 10 / 0; // 一定會丟擲異常

                             // 向inname轉入money 

                             accountDao.accountIn(inname, money);

                       }

 

     

this的用法:為當前類的物件的引用,當子類物件呼叫父類成員時,父類中的this相當於下面的f:

      Z繼承F

      F f=new Z();     這裡的f就類似於this

      此時this為子類物件的父類引用(多型),當呼叫getClass()方法時,獲取的時子類的位元組碼物件