有關於Spring對數據庫的操做屬於爲Spring中的Springdata模塊,對數據庫的操做。Spring對JDBC和Mybatis都有封裝與簡化html
能夠從如下角度學習研究java
SpringData:
1.對jdbc的操做來講,jdbc編程不變,主要是Connection對象的維護,即配置並使用數據源
2.spring在jdbc的使用中還給咱們提供了一個模板類:JdbcTemplate用以簡化咱們的jdbc操做
3.Spring與mybatis結合
4.Spring事務管理機制spring
第一部分 sql
對Connection對象的維護,即配置並使用數據源數據庫
1.基於jdk的規範數據源 --------jdk官方的數據源express
<bean name="dataSource1" class="oracle.jdbc.pool.OracleConnectionPoolDataSource"> <property name="networkProtocol"> <value>tcp</value> </property> <property name="databaseName"> <value>XE</value> </property> <property name="driverType"> <value>thin</value> </property> <property name="portNumber"> <value>1521</value> </property> <property name="user"> <value>briup</value> </property> <property name="serverName"> <value>127.0.0.1</value> </property> <property name="password"> <value>briup</value> </property> </bean>
!!!注意:別忘了讀取配置文件!!!apache
下面是配置文件的讀取方式編程
讀取這個資源文件 讀完以後下面就能夠用${key}來去文件中的value值了
這是第一種讀取資源文件的方式mybatis
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:oracle.properties" > </property> </bean>
這種方式是咱們第一種配置方式方式的簡寫併發
<context:property-placeholder location="classpath:oracle.perperties"/>
2.dbcp數據源---------第三方提供
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>${driver}</value> </property> <property name="url"> <value>${url}</value> </property> <property name="username"> <value>${user}</value> </property> <property name="password"> <value>${password}</value> </property> <!-- 最大鏈接數 --> <property name="maxActive"> <value>80</value> </property> <!-- 最大空閒鏈接數 --> <property name="maxIdle"> <value>20</value> </property> <!-- 最大等待時間:當沒有可用鏈接時,鏈接池等待鏈接被歸還的最大時間 單位:毫秒 --> <!-- 超過期間則拋出異常,若是設置爲-1表示無限等待 --> <property name="maxWait"> <value>3000</value> </property> </bean>
鏈接池基本的思想,原理:
在系統初始化的時候,將數據庫鏈接做爲對象存儲在內存中。
↓
當用戶須要訪問數據庫時,並不是創建一個新的鏈接,而是從鏈接池中取出一個已創建的空閒鏈接對象。
↓
數據庫鏈接池負責分配,管理和釋放數據庫鏈接,它容許應用程序重複使用一個現有的數據庫鏈接,而不是從新創建一個。
鏈接池通俗理解:
數據庫鏈接池就是準備一個池子,裏面放着好多好多生成好的Connection,用戶請求得到鏈接。
就不須要getConnection,只要從池子裏拿一個給他就好了,這樣省掉了生成Connection的時間。
效率上會有很大提升,不過固然會佔用一些內存~稍微大點網站都會用到數據庫鏈接池的~
3.spring提供的一種數據源--------Spring提供
這種數據源的配置比較簡單,通常也夠用
<bean id="dataSource3" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${driver}</value> </property> <property name="url"> <value>${url}</value> </property> <property name="username"> <value>${user}</value> </property> <property name="password"> <value>${password}</value> </property> </bean>
4.c3p0數據源----------鏈接池
<bean id="dataSource4" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>${driver}</value> </property> <property name="jdbcUrl"> <value>${url}</value> </property> <property name="user"> <value>${user}</value> </property> <property name="password"> <value>${password}</value> </property> <!--鏈接池中保留的最小鏈接數。 --> <property name="minPoolSize"> <value>5</value> </property> <!--鏈接池中保留的最大鏈接數。Default: 15 --> <property name="maxPoolSize"> <value>30</value> </property> <!--初始化時獲取的鏈接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 --> <property name="initialPoolSize"> <value>10</value> </property> <!--最大空閒時間,60秒內未使用則鏈接被丟棄。若爲0則永不丟棄。Default: 0 --> <property name="maxIdleTime"> <value>60</value> </property> <!--當鏈接池中的鏈接耗盡的時候c3p0一次同時獲取的鏈接數。Default: 3 --> <property name="acquireIncrement"> <value>5</value> </property> <!--每60秒檢查全部鏈接池中的空閒鏈接。Default: 0 --> <property name="idleConnectionTestPeriod"> <value>60</value> </property> <!--定義在從數據庫獲取新鏈接失敗後重復嘗試的次數。Default: 30 --> <property name="acquireRetryAttempts"> <value>30</value> </property> </bean>
能夠看出,將這些數據源做爲bean注入了框架中咱們須要從這些數據源中拿到Connection對象
能夠寫一個類來從鏈接池中得到connection對象:
public class DBUtil{ //數據源 private DataSource dataSorce; public DataSource getDataSorce() { return dataSorce; } public void setDataSorce(DataSource dataSorce) { this.dataSorce = dataSorce; } //得到Connection對象 public Connection getConn(){ Connection conn = dataSorce.getConnection(); return conn; } }
將這個類注入容器:
<bean name="dao" class="包名.DBUtil"> <property name="dataSorce" ref="dataSource4【數據源名】"></property> </bean>
使用getConn()方法得到Connection對象
public void jdbc_dataSource(){ try { String path = "xml文件地址"; ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext(path); DBUtil dao = (DBUtil)container.getBean("dao"); Connection conn = dao.getConn(); container.destroy(); }catch(Exception e) { e.printStackTrace(); } }
可是注意,通常來講,若是鏈接池設置了大小,這個鏈接拿到以後要注意歸還 【方法conn.close()】
第二部分
JdbcTemplate用以簡化咱們的jdbc操做
例如:
//java類中的寫法: public class JdbcTemplateDao implements AccountDao{ private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } ... }
xml中進行配置
<!-- spring提供的一種數據源 --> <bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${driver}</value> </property> <property name="url"> <value>${url}</value> </property> <property name="username"> <value>${user}</value> </property> <property name="password"> <value>${password}</value> </property> </bean> <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--很明顯這裏使用的是構造器注入,參數是一個數據源 也就是說,要使用這個類就先要在容器中注入一個Spring提供的數據源(這裏注意不僅是Spring提供的數據源有效)--> <constructor-arg index="0" ref="dataSource"></constructor-arg> </bean> <bean name="dao" class="com.briup.db.jdbc.JdbcTemplateDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean>
能夠寫個方法得到這個類對象:
public void jdbc_dataSource(){ try { String path = "xml文件地址"; ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext(path); JdbcTemplateDao dao = (JdbcTemplateDao)container.getBean("dao"); JdbcTemplate jdbcTemplate = dao.getJdbcTemplate(); container.destroy(); }catch(Exception e) { e.printStackTrace(); } }
JdbcTemplate這個類對象有一些方法用來操做sql語句【詳細方法能夠看Spring4.3.7說明書】
注意1:JdbcTemplate模板類如何使用:在htmlsingle中搜索便可,其中包含有大量的使用實例
注意2:spring結合jdbc的時候,不管是否使用這個模板,jdbc操做的事務默認是自動提交的(和以前學習jdbc的時候是一致的)
JDBC雖然不如Mybatis方便,可是效率的確高,在一些小型項目,或者是追求效率的問題中,使用Spring中的JdbcTemplate這個類簡化操做依舊是不錯的選擇。
第三部分
Spring與mybatis結合
首先我在classpath下建好一個配置文件【oracle.properties】內容以下
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:XE
user=briup
password=briup
讀取這個資源文件 讀完以後下面就能夠用${key}來去文件中的value值了
這種方式是簡寫
<context:property-placeholder location="classpath:oracle.properties" />
構建dbcp數據源(這裏注意不僅是dbcp的數據源有效)
<bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>${driver}</value> </property> <property name="url"> <value>${url}</value> </property> <property name="username"> <value>${user}</value> </property> <property name="password"> <value>${password}</value> </property> <!-- 最大鏈接數 --> <property name="maxActive"> <value>80</value> </property> <!-- 最大空閒鏈接數 --> <property name="maxIdle"> <value>20</value> </property> <!-- 最大等待時間:當沒有可用鏈接時,鏈接池等待鏈接被歸還的最大時間 單位:毫秒 --> <!-- 超過期間則拋出異常,若是設置爲-1表示無限等待 --> <property name="maxWait"> <value>3000</value> </property> </bean>
配置sqlSessionFactory
<bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.briup.db"></property> <property name="configurationProperties"> <props> <prop key="cacheEnabled">true</prop> </props> </property> <!-- 自動掃描mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/briup/db/mybatis/AccountMapper.xml" /> </bean>
自動掃描映射接口所在的包
未來能夠經過接口的名字首字母小寫做爲beanName,從spring容器中拿出自動生成的該接口的實現類
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.briup.db" /> </bean>
若是不加這個配置,同樣能夠經過前面的配置sqlSessionFactory拿到一個sqlSessionFactory對象,仍是能夠映射接口
固然也能夠本身寫一個mybatis-config.xml文件讀入,方法以下:
配置sqlSessionFactory 時使用mybatis-config.xml
直接讀取mybatis-config.xml文件,裏面和以前配置的同樣
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean>
最後還須要掃描mybatis中映射接口,以便spring爲其生產對應的實現類(這一步一樣能夠本身選擇的)
<!-- 自動掃描映射接口所在的包 --> <!-- 未來能夠經過接口的名字首字母小寫做爲beanName,從spring容器中拿出自動生成的該接口的實現類 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.briup.db" /> </bean>
第四部分
Spring事務管理機制
1.編程式事務管理(不經常使用)
所謂編程式事務指的是經過編碼方式實現事務。
記得在JDBC中可使用手動提交事務的方法
2.聲明式事務管理(經常使用)
在Spring配置文件中聲明式的處理事務來代替代碼式的處理事務.
在spring中,聲明式事務主要是經過【事務屬性】來定義的,事務屬性描述了事務策略如何應用到方法上面
事務屬性主要包含了如下5個方面:(文檔的最後有統一的介紹)
傳播行爲 隔離級別 回滾規則 事務超時 是否只讀
聲明式事務管理的配置方式一般如下幾種:
注意:配置事務的方式都須要用到事務管理器(切面)和事務攔截器(advice)
其實就是使用aop編程,把事務代碼動態織入到須要使用的方法上
spring中實現aop的配置方式不少,在這裏配置事務的時候推薦使用:
1.tx前綴的事務標籤和aop前綴的標籤結合,將切面(事務管理器)織入到切入點上
2.註解進行事務配置【在實際開發中更容易使用】
例如1:
spring結合jdbc,事務配置在service層指定方法上,使用tx標籤結合aop標籤
//使用jdbc實現dao層接口 public class JDBCAccountDaoImpl implements AccountDao{ private JdbcTemplate jdbcTemplate; //get/set方法 //實現接口中的抽象方法 ..... }
spring_jdbc.xml配置文件主要內容:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521:XE</value> </property> <property name="username"> <value>briup</value> </property> <property name="password"> <value>briup</value> </property> </bean>
jdbc_service1.xml配置文件主要內容:
<!-- 配置dao層對象 --> <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg index="0" ref="dataSource"></constructor-arg> </bean> <bean id="accountDao" class="com.briup.tran.jdbc.JDBCAccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <!-- 配置service層對象 目標對象--> <bean name="service" class="com.briup.tran.service.AccountServiceImpl"> <!-- 注入dao層對象 --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 配置jdbc的事務管理器 (切面類)--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入數據源 --> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事務攔截器 --> <tx:advice id="transactionInterceptor" transaction-manager="transactionManager"> <tx:attributes> <!-- *表明全部的方法 --> <tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/> </tx:attributes> </tx:advice> <!-- 配置aop --> <aop:config> <!-- 配置切入點 --> <aop:pointcut expression="execution(public * com.briup.tran.service.*.*(..))" id="myPointCut"/> <!-- 配置事務攔截器在哪個切入點上起做用 --> <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myPointCut"/> </aop:config>
測試方法:
String path[] = {"com/briup/tran/jdbc/spring_jdbc.xml", "com/briup/tran/service/jdbc_service1.xml"}; ApplicationContext container = new ClassPathXmlApplicationContext(path); AccountService service = (AccountService)container.getBean("service"); service.add(new Account(1,"zs1",1000));
例如2:
spring結合jdbc,事務配置在service層指定方法上,使用註解進行事務配置
和例子1中只有倆處不一樣
第一處:
service接口的實現類的上面使用@Transactional:這個註解加在目標類上
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Throwable.class) public class AccountServiceImpl implements AccountService{ ..... }
第二處:
jdbc_service2.xml配置文件主要內容:
<!-- 配置service層對象 目標對象--> <bean name="service" class="com.briup.tran.service.AccountServiceImpl"> <!-- 注入dao層對象 --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 配置jdbc的事務管理器 (切面類)--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入數據源 --> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 通知spring咱們在目標對象中作了事務的註解,並指明使用哪個事務管理器 --> <!-- 加入上這個標籤後 去目標對象去加入相應的註解就能夠了 --> <tx:annotation-driven transaction-manager="transactionManager"/>
例如3:
spring結合mybatis,事務配置在service層指定方法上,使用tx標籤結合aop標籤
AccountMapper.xml配置文件主要內容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- com.briup.tran.dao.AccountDao是咱們定義接口的全限定名字 這樣就可使用接口調用映射的SQL語句了 這個名字必定要和接口對應上--> <mapper namespace="com.briup.tran.dao.AccountDao"> <insert id="save" parameterType="Account"> insert into t_account(id,name,balance) values(#{id},#{name},#{balance}) </insert> <update id="update" parameterType="Account"> update t_account set name=#{name}, balance=#{balance} where id=#{id} </update> <delete id="delete" parameterType="Account"> delete from t_account where id=#{id} </delete> </mapper>
spring_mybatis.xml配置文件主要內容:
<!-- 配置數據源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521:XE</value> </property> <property name="username"> <value>briup</value> </property> <property name="password"> <value>briup</value> </property> </bean> <!-- 配置sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.briup.tran"></property> <property name="configurationProperties"> <props> <prop key="cacheEnabled">true</prop> </props> </property> <!-- 自動掃描mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/briup/tran/mybatis/AccountMapper.xml" /> </bean> <!-- 自動掃描映射接口所在的包 --> <!-- 未來能夠經過接口的名字首字母小寫做爲beanName,從spring容器中拿出自動生成的該接口的實現類 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.briup.tran.dao" /> </bean>
mybatis_service1.xml配置文件主要內容:
<!-- 配置service層對象 目標對象--> <bean name="service" class="com.briup.tran.service.AccountServiceImpl"> <!-- 注入dao層對象 --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 配置jdbc的事務管理器 (切面類) 適用於mybatis--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入數據源 --> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事務攔截器 --> <tx:advice id="transactionInterceptor" transaction-manager="transactionManager"> <tx:attributes> <!-- *表明全部的方法 --> <tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/> </tx:attributes> </tx:advice> <!-- 配置aop --> <aop:config> <!-- 配置切入點 --> <aop:pointcut expression="execution(public * com.briup.tran.service.*.*(..))" id="myPointCut"/> <!-- 配置事務攔截器在哪個切入點上起做用 --> <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myPointCut"/> </aop:config>
測試方法:
.....
例如4:
spring結合mybatis,事務配置在service層指定方法上,使用註解進行事務配置
和例子3中只有倆處不一樣
第一處:
service接口的實現類的上面使用@Transactional:
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Throwable.class) public class AccountServiceImpl implements AccountService{ ..... }
第二處:
mybatis_service2.xml配置文件主要內容:
<!-- 配置service層對象 目標對象--> <bean name="service" class="com.briup.tran.service.AccountServiceImpl"> <!-- 注入dao層對象 --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 配置jdbc的事務管理器 (切面類) 適用於mybatis--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入數據源 --> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 通知spring咱們在目標對象中作了事務的註解,並指明使用哪個事務管理器 --> <!-- 加入上這個標籤後 去目標對象去加入相應的註解就能夠了 --> <tx:annotation-driven transaction-manager="transactionManager"/>
-----------------------------------------------------------
瞭解事務屬性包含的五個方面分別是什麼:
1)事務傳播行爲
規定了若是有新的事務應該被啓動仍是被掛起,或者方法是否須要在事務中運行。
TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
2)事務隔離級別
定義了一個事務可能受其餘併發事務影響的程度。
隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,一般這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務能夠讀取另外一個事務修改但尚未提交的數據。該級別不能防止髒讀和不可重複讀,所以不多使用該隔離級別。
TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另外一個事務已經提交的數據。該級別能夠防止髒讀,這也是大多數狀況下的推薦值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程當中能夠屢次重複執行某個查詢,而且每次返回的記錄都相同。即便在屢次查詢之間有新增的數據知足該查詢,這些新增的記錄也會被忽略。該級別能夠防止髒讀和不可重複讀。
TransactionDefinition.ISOLATION_SERIALIZABLE:全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。
注意:不一樣的數據庫所能支持的事務隔離級別以及默認的事務隔離級別有多是不一樣的
3)事務的只讀屬性
定義了一個事務中是不是隻讀操做,若是設置只讀那麼數據庫內部就能夠對該操做進行合適的優化措施,只有傳播行爲是PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_NESTED的時候只讀設置纔有意義,由於只讀優化是在事務開始的時候由數據庫實施的,而在這三個傳播行爲下才有可能啓動一個新事務
4)事務超時
爲了使應用程序能夠很好的運行,事務不能運行太長的時間,因此這個屬性就控制着這個時間.只有傳播行爲是PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_NESTED的時候超時設置纔有意義,由於超時時鐘會在事務開始的時候啓動,而在這三個傳播行爲下才有可能啓動一個新事務.注意事務超時後會自動回滾.(單位是 秒)
5)事務的回滾規則
定義了哪些異常會致使事務回滾而哪些不會。默認狀況下,事務在遇到運行時異常的時候纔會回滾,而遇到檢查時異常時不會回滾
-Exception表示有Exception拋出時,事務回滾. -表明回滾+就表明提交