Spring提供了一個jdbc模板,它類似於dbutils工具。
快速入門
創建數據庫
CREATE DATABASE springtest; USE springtest; CREATE TABLE t_user( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT, sex VARCHAR(20) ) INSERT INTO t_user VALUES(NULL,'tom',20,'男'); INSERT INTO t_user VALUES(NULL,'fox',30,'男'); INSERT INTO t_user VALUES(NULL,'tony',40,'男'); SELECT * FROM t_user;
C3P0開源連接池配置
<!-- 配置連接池對象 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="jdbc:mysql:///spring"></property> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean>
為了便於修改
引入外部屬性文件,在src下的db.properties文件
jdbc.jdbcUrl=jdbc:mysql:///spring
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root
在applicationContext.xml文件中引入
<context:property-placeholder location="classpath:db.properties"/>
所以C3P0連接池的引入可以改為
<!-- 配置c3p0連接池 --> <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
在自己配置中需要從properties文件中引入的信息可以使用${name}方式來獲取
JdbcTemplate CRUD
執行insert update delete操作
只需要使用JdbcTemplate的update方法就可以執行insert update delete操作
@Autowired @Qualifier("jdbcTemplate") private JdbcTemplate template; @Test public void test1() { // update執行update操作 template.update("update t_user set name = ? where id = ?", "猥瑣逼", 1); } @Test public void test2() { // update執行update操作 template.update("insert into t_user values(null,?,?,?)", "龜哥", 25, "男"); } @Test public void test3() { // update執行delete操作 template.update("delete from t_user where id = ?", 4); }
執行select操作
簡單數據返回
/* * 下面測試查詢語句返回簡單的數據類型 */ @Test public void test4() { String name = template.queryForObject("select name from t_user where id = 1", String.class); System.out.println(name); } @Test public void test5() { Integer count = template.queryForObject("select count(*) from t_user", Integer.class); System.out.println(count); }
復雜數據返回
/* * 下面測試復雜返回類型 */ @Test public void test6() { // 如果返回的是一個對象,可以使用queryForObject方法 User user = template.queryForObject("select * from t_user where id = ?", new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int arg1) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setAge(rs.getInt("age")); user.setSex(rs.getString("sex")); return user; } },1); System.out.println(user); } @Test public void test7() { // 如果返回的是一個集合,使用query方法 List<User> list = template.query("select * from t_user", new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int arg1) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setAge(rs.getInt("age")); user.setSex(rs.getString("sex")); return user; } }); System.out.println(list); }
注意:如果只返回一個domain對象,可以使用queryForObject方法,如果返回的是List<?>對象,
可以使用query方法,但是都需要使用RowMapper來對ResultSet進行處理。
RowMapper它有一個實現類叫BeanPropertyRowMapper
如果使用BeanPropertyRowmapper,實體類必須提供一個無參數的public構造方法,類中的bean屬性名稱與表中的列要對應
@Test public void test8() { // 使用rowMapper的實現類BeanPropertyRowMapper /*List<User> list = template.query("select * from t_user", new BeanPropertyRowMapper<User>(User.class)); System.out.println(list);*/ List<User> user = template.query("select * from t_user where id = 1", new BeanPropertyRowMapper<User>(User.class)); System.out.println(user); }
spring的事務管理
下面通過一個轉賬案例來了解spri
創建數據庫表
CREATE TABLE account( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), money DOUBLE )
INSERT INTO account VALUES(NULL,'tom',1000);
INSERT INTO account VALUES(NULL,'fox',1000);
創建service於dao
@Service("accountService") public class AccountServiceImpl implements AccountService{ @Autowired @Qualifier("accountDao") private AccountDaoImpl ad; @Override public void account(String outname, String inname, double money) { ad.accountIn(inname,money); //int a = 10 /0; ad.accountOut(outname,money); } }
@Repository("accountDao") public class AccountDaoImpl implements AccountDao{ @Autowired @Qualifier("jdbcTemplate") private JdbcTemplate temple; @Override public void accountIn(String inname, double money) { temple.update("update account set money = money + ? where name = ?",money,inname); } @Override public void accountOut(String outname, double money) { temple.update("update account set money = money - ? where name = ?",money,outname); } }
不要忘了在applicationContext中配置開啟注解
<!-- 開啟注解配置 -->
<context:component-scan base-package="com.learn"></context:component-scan>
如果打開//int a = 10 /0;
就一定會排出異常
這時我們就需要事務控制,讓事務具有一致性
Spring事務管理主要提供了三個接口來完成
1. org.springframework.transaction.PlatformTransactionManager
這是一個事務管理器,可以來選擇相關的平台(jdbc hibernate jpa…)
2. TransactionDefinition
它定義事務的一些相關信息 例如 隔離 傳播 超時 只讀
3. TransactionStatus
它主要描述事務具體的運行狀態
PlatformTransactionManager:
平台事務管理器,在不同的持久化層解決技術它的事務代碼不一樣。
DataSourceTransactionManager 主要針對於JdbcTemplate開發 MyBatis開發
HibernateTransactionManasger主要針對於Hibernate開發
JpaTransactionManager 主要針對於JPA開發。
TransactionDefinition:
它描述的是事務的定義信息。
在TransactionDefinition中定義了大量的常量
事務的四個特性 ACID 原子性 一致性 隔離性 持久性。
不考慮事務隔離性會出現:臟讀,不可重復讀 虛讀。
ISOLATION_DEFUALT 它使用后端數據庫的默認隔離級別(spring中選項)
ISOLATION_READ_UNCOMMITTED 不能解決問題,會發生臟讀 不可重復讀 虛讀
ISOLATION_READ_COMMITTED 可以解決臟讀 會產生不可重復讀與虛讀。
ISOLATION_REPEATABLE_READ 可以解決臟讀,不可重復讀 解決不了虛讀
ISOLATION_SERIALIZABLE 串行化,可以解決所有問題
對於不現的數據庫,它的底層默認事務隔離級別不一樣。
Oracle數據庫它默認的是read_committed
Mysql數據庫它默認的是repeatable_read.
下面再介紹一下它的傳播特性:
它解決的是兩個被事務管理的方法互相調用問題。它與數據庫沒關系,是程序內部維護的問題。
最常用的三種:
PROPAGATION_REQUIRED 默認值 兩個操作處於同一個事務,如果之前沒有事務,新建一個事務
PROPAGATION_REQUIRES_NEW,兩個操作處於不同的事務
PROPAGATION_NESTED,它是一種嵌套事務,它是使用SavePoint來實現的。
事務回滾時可以回滾到指定的savepoint,注意:它只對DataSourceTransactionManager有作用
TransactionStatus:
它定義了事務狀態信息,在事務運行過程中,得到某個時間點的狀態
事務管理方式
1. 編碼方案 不建議使用,它具有侵入性。在原有的業務代碼基礎上去添加事務管理代碼
2. 聲明式事務控制,基於AOP對目標進行代理,添加around環繞通知。
這種方案,它不具有侵入性,不需要修改原來的業務代碼
基於xml配置聲明式事務管理方案
<!-- 配置開啟事務 --> <!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="c3p0DataSource"></property> </bean> <!-- 配置通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- name:必須的,對哪些方法進行事務控制 isolation 可選 設置事務隔離級別 默認是DEFAULT propagation:可選 設置事務傳播 默認值 REQUIRED timeout 可選 超時時間 默認值-1 read-only 可選 默認值是false 如果不是只讀,它可以對insert update delete操作,如果是只讀不可以。 rollback-for 可選 可以設置一個異常,如果產生這個異常,觸發事務回滾 no-rolback-for 可選 可以設置一個異常,如果產生這個異常,不會觸發事務回滾 --> <tx:method name="account" /> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <aop:pointcut expression="execution(* com.learn.service.AccountService.account(..))" id="txPointcut" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /> </aop:config>
基於annotation聲明式事務管理方案
<!-- 開啟注解配置事務 --> <tx:annotation-driven transaction-manager="transactionManager"/>
在需要開啟的類或方法上聲明
@Transactional