本篇隨筆是上兩篇的延續:三種數據庫訪問——原生JDBC;數據庫鏈接池:Druidhtml
Spring主要提供JDBC模板方式、關係數據庫對象化方式、SimpleJdbc方式、事務管理來簡化JDBC編程java
Spring提供了3個模板類:mysql
JdbcTemplate主要提供如下4類方法:spring
接下來,經過一個示例項目來展現如何使用Spring的JDBC框架訪問數據庫。假設該項目的功能有:保存用戶信息、查詢用戶信息。sql
CREATE TABLE `user` ( `id` int(10) NOT NULL auto_increment, `name` varchar(30) default NULL, `age` int(3) default NULL, PRIMARY KEY (`id`) )
依賴配置:數據庫
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>0.2.25</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>3.2.4.RELEASE</version> </dependency>
依賴的包有:Junit、mysql驅動器、druid(阿里巴巴開發的高性能的數據庫鏈接池)、spring-context、spring-jdbc編程
public class User implements Serializable{ private Long id; private String name; private Integer age; //setter getter 略 public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
package edu.shao.springJdbc.dao; import java.util.List; import edu.shao.springJdbc.po.User; public interface IUserDao { public void save(User user); public List<User> query(String sql,Object[] args); }
package edu.shao.springJdbc.dao.impl; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; import edu.shao.springJdbc.dao.IUserDao; import edu.shao.springJdbc.po.User; public class UserDaoImpl extends JdbcDaoSupport implements IUserDao { class UserRowMapper implements RowMapper<User> { //實現ResultSet到User實體的轉換 public User mapRow(ResultSet rs, int rowNum) throws SQLException { User m = new User(); m.setId(rs.getLong("id")); m.setName(rs.getString("name")); m.setAge(rs.getInt("age")); return m; } }; public void save(User model) { getJdbcTemplate().update("insert into user(name,age) values(?,?)", model.getName(), model.getAge()); } public List<User> query(String sql, Object[] args) { return getJdbcTemplate().query(sql, args, new UserRowMapper()); } }
package edu.shao.springJdbc.service; public interface IUserService { void saveUser(); void saveUserThrowException() throws Exception; void findUsers(); }
package edu.shao.springJdbc.service.impl; import java.util.List; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import edu.shao.springJdbc.dao.IUserDao; import edu.shao.springJdbc.po.User; import edu.shao.springJdbc.service.IUserService; @Transactional public class UserServiceImpl implements IUserService { private IUserDao userDao; public void saveUser() { User u1=new User(); u1.setName("邵"); u1.setAge(24); userDao.save(u1); if(1+1>1){ throw new RuntimeException("Runtime error...");//拋出運行時異常:RuntimeException } User u2=new User(); u2.setName("陳"); u2.setAge(20); userDao.save(u2); } public void saveUserThrowException() throws Exception { User u1=new User(); u1.setName("邵"); u1.setAge(24); userDao.save(u1); if(1+1>1){ throw new Exception("Runtime error...");//拋出通常的異常:Exception } User u2=new User(); u2.setName("陳"); u2.setAge(20); userDao.save(u2); } @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) public void findUsers() { List<User> users=userDao.query("select * from user where age>?", new Object[]{17}); for (User user : users) { System.out.println(user); } } //setter getter略 }
Spring對事務的管理有豐富的支持,Spring提供了編程式配置事務和聲明式配置事務,其中,聲明式事務有如下兩種方式 :併發
(編程式的事務處理有些侵入性。一般咱們的事務需求並無要求在事務的邊界上進行如此精確的控制,咱們通常採用"聲明式事務"。)app
上面咱們採用了基於註解的方式來配置事務。框架
applicationContext-dataSource.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="123456" /> <property name="initialSize" value="1" /> <property name="maxActive" value="20" /> </bean> </beans>
applicationContext-jdbc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <import resource="applicationContext-dataSource.xml" /> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 首先定義抽象的abstractDao,其有一個jdbcTemplate屬性,從而可讓繼承的子類自動繼承jdbcTemplate屬性注入; --> <bean id="abstractDao" abstract="true"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> <bean id="userDao" class="edu.shao.springJdbc.dao.impl.UserDaoImpl" parent="abstractDao" /> <bean id="userService" class="edu.shao.springJdbc.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="txManager" /> </beans>
上面<tx:annotation-driven transaction-manager="txManager" /> 這句話的做用是註冊事務處理器。
咱們只須要在類上加上註解@Transactional,就能夠指定這個類須要受Spring的事務管理。默認Spring爲每一個方法開啓一個事務,若是方法發生運行期異常(RuntimeException),事務會進行回滾;若是發生通常的異常(Exception),事務不進行回滾。
package edu.shao.springJdbc; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import edu.shao.springJdbc.service.IUserService; public class SpringJdbcTest { private static ApplicationContext ctx = null; @BeforeClass //表示在因此測試方法以前執行,且只執行一次。 public static void onlyOnce() { ctx = new ClassPathXmlApplicationContext("db/applicationContext-jdbc.xml"); } @Test public void testSave(){ IUserService service=ctx.getBean("userService",IUserService.class); service.saveUser(); } @Test public void testSaveThrowException() throws Exception{ IUserService service=ctx.getBean("userService",IUserService.class); service.saveUserThrowException(); } @Test public void testJDBCDaoQuery(){ IUserService service=ctx.getBean("userService",IUserService.class); service.findUsers(); } }
運行測試類,
第一個測試方法,後臺輸出異常:java.lang.RuntimeException,查看數據庫發現數據沒有插入,說明事務進行了回滾。
第二個測試方法,後臺輸出異常:java.lang.Exception ,查看數據庫發現第一條數據插入,而第二條數據沒有插入,說明事務沒有進行了回滾。
說明了Spring的事務支持默認只對運行期異常(RuntimeException)進行回滾,若是執行sql操做的時候會發生sql異常,不屬於運行期異常,那Spring是怎麼進行事務回滾的呢 ?
Spring把SQLException等異常轉化爲了DataAccessException,後者是一種RuntimeException,因此只對RuntimeException異常進行回滾是很合理的。
其餘註解方式:
關於事務的傳播屬性有下面幾種配置:
總結:
事務是企業應用開發的重要組成部分,它使軟件更加可靠。它們確保一種要麼全有 要麼全無的行爲,防止數據不一致而致使的不可預測的錯誤發生。 事務同時也支持併發,防止併發應用線程在操做同一數據時互相影響。
之前咱們寫Jdbc代碼的時候,可能須要本身手動去開啓事務,而後方法執行結束以後再去提交事務,所有都嵌套在咱們的業務代碼之中,具備很強的侵入性....
使用Spring提供事務管理機制,咱們只須要配置XML或使用Annotion進行註解就能夠實現事務的管理和配置,減小了代碼之間的耦合,配置也很方便,很大程度上提高了咱們的開發效率。