1. Spring的DAO理念
Spring提供了一套抽象的DAO類,供開發者擴展,這有利於以統一的方式操作各種DAO技術,如JDO、JDBC等,這些抽象DAO類提供了設置數據源及相關輔助信息的方法,而其中的一些方法同具體DAO技術相關。目前,SpringDAO抽象提供了以下幾種類。
- JdbcDaoSupport:JDBC DAO抽象類,開發者需要為它設置數據源(DataSource),通過子類,開發者能夠獲得JdbcTemplate來訪問數據庫。
- HibernateDaoSupport:Hibernate DAO抽象類。開發者需要為它配置Hibernate SessionFactory。通過其子類,開發者能夠獲得Hibernate的實現。
- JdoDaoSupport:Spring為JDO提供了DAO抽象類,開發者需要為它配置PersistenceManagerFactory,通過其子類開發者能夠獲得JdoTemplate。
在使用Spring的DAO框架進行數據庫存取時,無須接觸使用特定的數據庫技術,通過一個數據存取接口來操作即可。
例1.1 在Spring中利用DAO模式向tb_user表中添加數據。
(1)定義一個實體類對象User,然后在類中定義對應的數據庫表字段的屬性。關鍵代碼如下:
package com.cn.aop; public class User { private Integer id; private String name; private Integer age; private String sex; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
(2)創建接口UserDAOImpl,並定義用來執行數據添加的insertUser()方法。其中insertUser()方法中使用的參數是User實體對象。代碼如下:
package com.cn.aop; public interface UserDAOImpl { public void insertUser(User user); //添加用戶信息的方法 }
(3)編寫實現這個DAO接口的UserDAO類,並在該類中實現接口中定義的方法。首先定義一個用於操作數據庫的數據源對象DataSource,通過它創建一個數據庫連接對象建立與數據庫的連接,這個數據源對象在Spring中提供了javax.sql.DataSource接口的實現,只需要在Spring的配置文件中進行相關的配置即可,稍后會有關於Spring的配置文件。這個類中實現了接口的抽象方法insertUser()方法,通過這個方法訪問數據庫,關鍵代碼如下:
package com.cn.aop; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import java.sql.PreparedStatement; public class UserDAO implements UserDAOImpl { private DataSource dataSource; public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } //向數據表tb_user中添加數據 @Override public void insertUser(User user) { String name = user.getName(); Integer age = user.getAge(); String sex = user.getSex(); Connection conn = null; PreparedStatement ps = null; try { //獲取數據庫連接 conn = dataSource.getConnection(); //添加數據的SQL語句 String sql = "insert into tb_user(name,age,sex) values(?,?,?)"; ps = conn.prepareStatement(sql); ps.setString(1, name); ps.setInt(2, age); ps.setString(3, sex); //執行SQL語句 ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { ps.close(); conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
(4)編寫Spring的配置文件applicationContext.xml,在這個配置文件中首先定義一個JavaBean名稱為DataSource的數據源,它是Spring中的DriverManagerDataSource類的實例。然后再配置前面編寫完userDAO類,並且注入它的DataSource屬性值。具體配置代碼如下:
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 配置數據源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/test</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>1234</value> </property> </bean> <!-- 為UserDAO注入數據源 --> <bean id="userDAO" class="com.cn.aop.UserDAO"> <property name="dataSource"> <ref local="dataSource"/> </property> </bean> </beans>
(5)創建類Manager,在其main()方法中的關鍵代碼如下:
package com.cn.aop; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Manger { public static void main(String[] args){ //裝載配置文件 Resource resource = new ClassPathResource("applicationContext.xml"); BeanFactory factory = new XmlBeanFactory(resource); //實例化User對象 User user = new User(); user.setName("張三"); user.setAge(new Integer(30)); user.setSex("男"); //獲取UserDAO UserDAO userDAO = (UserDAO) factory.getBean("userDAO"); //執行添加方法 userDAO.insertUser(user); System.out.println("數據添加成功!"); } }
程序運行后,數據表tb_user添加的數據如下圖所示:
2. 事務應用的管理
事務的管理通常分為兩種方式,即編程式事務管理和聲明式事務管理,在Spring中這兩種事務管理方式都非常優秀。
- 編程式事務管理
在Spring中主要有兩種編程式事務的實現方法,分別使用PlatformTransactionManager接口的事務管理器或TransactionTemplate實現。雖然兩者各有優缺點,但是推薦使用TransactionTemplate實現方式,因為它符合Spring的模板模式。
說明:TransactionTemplate模板和Spring的其他模板一樣,它封裝了資源的打開和關閉等常用的重復代碼,在編寫程序的時候只需要完成需要的業務代碼即可。
例2.1 利用TransactionTemplate實現Spring編程式事務管理。
(1)首先要在Spring的配置文件中聲明事務管理器和TransactionTemplate並為TransactionExample配置相關信息,關鍵代碼如下:
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 配置數據源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/test</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>1234</value> </property> </bean> <!-- 定義TransactionTemplate模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager"> <ref bean="transactionManager"/> </property> <property name="propagationBehaviorName"> <value>PROPAGATION_REQUIRED</value> </property> </bean> <!-- 定義事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"></ref> </property> </bean> <!-- 為TransactionExample注入數據源 --> <bean id="transactionExample" class="com.cn.aop.TransactionExample"> <property name="dataSource"> <ref local="dataSource"/> </property> <property name="transactionManager"> <ref local="transactionManager"/> </property> <property name="transactionTemplate"> <ref local="transactionTemplate"/> </property> </bean> </beans>
(2)創建類TransactionExample,定義數據添加的方法,在方法中執行兩次數據庫添加的操作,並用事務對操作進行保護。關鍵代碼如下:
package com.cn.aop; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; public class TransactionExample { DataSource dataSource; //注入數據源 PlatformTransactionManager transactionManager; //注入事務管理器 TransactionTemplate transactionTemplate; //注入TransactionTemplate模板 public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public PlatformTransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public TransactionTemplate getTransactionTemplate() { return transactionTemplate; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } public void transactionOperation() { transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus status) { Connection conn = DataSourceUtils.getConnection(dataSource); //獲得數據庫連接 try { Statement stmt = conn.createStatement(); //執行兩次添加方法 stmt.execute("insert into tb_user(name,age,sex) values('小強','26','男')"); stmt.execute("insert into tb_user(name,age,sex) values('小紅','22','女')"); System.out.println("操作執行成功!"); } catch (SQLException e) { transactionManager.rollback(status); System.out.println("操作執行失敗,事務回滾!"); System.out.println("原因:"+e.getMessage()); } return null; } }); } }
(3)創建類Manger,在其main()方法中的代碼如下:
package com.cn.aop; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Manger2 { public static void main(String[] args) { //裝載配置文件 Resource resource = new ClassPathResource("applicationContext.xml"); BeanFactory factory = new XmlBeanFactory(resource); //獲取TransactionExample TransactionExample transactionExample = (TransactionExample) factory.getBean("transactionExample"); //執行添加方法 transactionExample.transactionOperation(); } }
(4)為了測試事務是否配置正確,在transactionOperation()方法中執行兩次添加操作的語句之間添加兩句代碼,制造人為異常。也就是說,當第一條操作語句執行成功后,第二天語句因為程序的異常無法執行成功,這種情況下如果事務成功回滾說明事務配置成功,添加的代碼如下:
int a=0; a=8/a;
程序執行后控制台會輸出相關信息。
- 聲明式事務管理
Spring的聲明式事務不涉及組建依賴關系,它通過AOP實現事務管理,Spring本身就是一個容器,相對EJB容器而言,Spring顯得更為輕便小巧。在使用Spring的聲明式事務時無須編寫任何代碼,便可通過實現基於容器的事務管理。Spring提供了一些可供選擇的輔助類,這些輔助類簡化了傳統的數據庫操作流程,在一定程度上節省了工作量,提高了編程效率,所以推薦使用聲明式事務。
在Spring中常用TransactionProxyFactoryBean完成聲明式事務管理。
例2.2 利用TransactionProxyFactoryBean實現Spring聲明式事務管理。
(1)首先在配置文件中定義數據源DataSource和事務管理器,這個事務管理器被注入到TransactionProxyFactoryBean中,設置代理對象和事務屬性。這里的目標對象是以內部類的方式定義的。配置文件中的關鍵代碼如下:
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 配置數據源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/test</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>1234</value> </property> </bean> <!-- 定義事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"></ref> </property> </bean> <!-- 定義TransactionProxy --> <bean id="transactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="transactionManager"/> </property> <property name="target"> <bean id="addDAO" class="com.cn.dao.AddDAO"> <property name="dataSource"> <ref local="dataSource"/> </property> </bean> </property> <property name="proxyTargetClass" value="true"/> <property name="transactionAttributes"> <props> <prop key="add*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
(2)其次編寫操作數據庫的AddDAO類,該類中的addUser()方法是關鍵,在該方法中執行了兩次數據的插入操作,其在配置TransactionProxyFactoryBean時被定義為事務性方法,並指定了事務屬性,所以方法中的所有數據庫操作都被當作一個事務處理。類中的代碼如下:
package com.cn.dao; import org.springframework.jdbc.core.support.JdbcDaoSupport; import com.cn.aop.User; public class AddDAO extends JdbcDaoSupport { //添加用戶方法 public void addUser(User user) { //執行添加的SQL語句 String sql = "insert into tb_user(name,age,sex) values('"+ user.getName()+"','"+user.getAge()+"','"+user.getSex()+"')"; //執行兩次添加方法 getJdbcTemplate().execute(sql); getJdbcTemplate().execute(sql); } }
(3)創建類Manger,在其main()方法中的代碼如下:
package com.cn.dao; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import com.cn.aop.AddDAO; import com.cn.aop.User; public class Manger { public static void main(String[] args) { //裝載配置文件 Resource resource = new ClassPathResource("applicationContext.xml"); BeanFactory factory = new XmlBeanFactory(resource); //獲得AddDAO AddDAO addDAO = (AddDAO) factory.getBean("transactionProxy"); //實例化User對象 User user = new User(); user.setName("張三"); user.setAge(new Integer(30)); user.setSex("男"); //執行數據庫添加方法 addDAO.addUser(user); } }