Spring DAO模組
Spring的DAO模組提供了對JDBC、Hibernate、Mybatis等DAO層支援,本節介紹DAO模組對JDBC的支援。DAO模組依賴ommons-dbcp.jar、commons-pool.jar。
一、JdbcDaoSupport的使用
傳統的JDBC時需要建立連線、開啟、執行sql、關閉連線這一系列的步驟,Spring框架對JDBC進行了封裝,我們只需使用封裝好的JdbcTemplate執行sql語句。既然是JdbcDaoSupport的使用,為什麼是使用JdbcTemplate呢?因為JdbcDaoSupport提供了JdbcTemplate物件,通過JdbcTemplate物件進行資料庫操作。可以轉到定義,檢視JdbcDaoSupport、JdbcTemplate兩個類的具體實現。我們通過下面的例子來了解JdbcDaoSupport的使用,這裡還是使用JDBC章節的資料庫daodemodb和表t_user資訊。
第一步,根據t_user表資訊準備Model類User。定義了id、name、age、money屬性,並聲明瞭兩個建構函式。
package com.demo.model; public class User { @Override public String toString() { return "Id:"+this.getId()+" Name:"+this.getName()+" Age:"+this.getAge()+" Money:"+this.getMoney(); } private int Id; private String Name; private int Age; private double Money; public User() { } public User(String name, int age, double money) { Name = name; Age = age; Money = money; } public int getId() { return Id; } public void setId(int id) { Id = id; } public String getName() { return Name; } public void setName(String name) { Name = name; } public int getAge() { return Age; } public void setAge(int age) { Age = age; } public double getMoney() { return Money; } public void setMoney(double money) { Money = money; } } View Code
第二步,定義介面類IUserDAO,在介面中聲明瞭兩個方法,QueryAllUser方法屬於查詢操作,查詢所有User,AddUser屬於更新操作,新增User。
package com.demo.model; import java.util.*; public interface IUserDAO { public List<User>QueryAllUser(); public Boolean AddUser(User user); public Boolean transfer(int fromUserId, int toUserId, float transferMoney); } View Code
第三步,就是JdbcDaoSupport的使用了,在下面的SpringDAODemo類中首先繼承了JdbcDaoSupport,同時實現了IUserDAO 介面中的方法。JdbcDaoSupport提供了JdbcTemplate物件,SpringDAODemo繼承了JdbcDaoSupport,所以也就可以直接獲取到JdbcTemplate物件,然後執行該物件的方法進行資料庫操作。
package com.demo.model; import java.util.*; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class SpringDAODemo extends JdbcDaoSupport implements IUserDAO { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"}); BeanFactory factory=context; IUserDAO userDao=(IUserDAO)factory.getBean("userDao"); User user=new User("JdbcDaoSupportTest",26,333.33); userDao.AddUser(user); List<User> list=userDao.QueryAllUser(); for(User u:list) { System.out.println(u.toString()); } } public List<User> QueryAllUser() { String sql="select id,name,age,money from t_user order by id desc"; List<Map<String,Object>> list=getJdbcTemplate().queryForList(sql); List<User> userList=new ArrayList<User>(); for(Map<String,Object> row:list) { User user=new User(); user.setId((Integer)row.get("id")); user.setName((String)row.get("name")); user.setAge((Integer)row.get("age")); user.setMoney(Double.parseDouble(row.get("money").toString())); userList.add(user); } return userList; } public Boolean AddUser(User user) { String sql="insert into t_user (name,age,money) values (?,?,?)"; int row=getJdbcTemplate().update(sql, new Object[]{user.getName(),user.getAge(),user.getMoney()}); if(row>0) { System.out.println("資料新增成功!"); return true; } return false; } } View Code
第四步,配置屬性。在上面的main方法中先通過上下文獲取到bean物件,然後執行新增操作和查詢操作。但是上面的程式碼並未看到資料庫資訊,這裡還需要在ApplicationContext.xml中配置資料庫資訊,併為JdbcDaoSupportDemo設定資料來源。為什麼會有dataSource屬性呢,因為JdbcDaoSupport中包含這個屬性。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://127.0.0.1:3306/daodemodb</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>123456</value> </property> </bean> <bean id="userDao" class="com.demo.model.SpringDAODemo" depends-on="dataSource"> <property name="dataSource" ref="dataSource"></property> </bean> </beans> View Code
第五步,pom.xml中配置的依賴資訊。例子是Spring中DAO的實現,而且是對jdbc的封裝,所以包含Spring相關依賴和jdbc的相關依賴。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>SpringDAO</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>5.0.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies> </project> View Code
執行SpringDAODemo,可以發現數據庫和列印結果中增加了一條記錄。
二.MappingSqlQuery的使用
在JdbcDaoSupport的使用小節獲取所有User的方法QueryAllUser()中,使用getJdbcTemplate().queryForList()返回的是List<Map<String,Object>>型別,需要遍歷轉換成Java物件,那問題來了,查詢的不止這一個方法,可能以後會有條件查詢的方法,那每次都要把從資料庫返回的List<Map<String,Object>>型別的List轉一遍,當然也可以專門寫一個轉換的方法,這樣每次傳List<Map<String,Object>>型別的引數,然後返回List<User>型別的值。其實還有一種方式,就是使用MappingSqlQuery。MappingSqlQuery是一個抽象類,需要實現它的方法mapRow()。
第一步,實現MappingSqlQuery抽象類,這裡在UserMappingSqlQuery類中實現了mapRow()方法。
package com.demo.model; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.object.MappingSqlQuery; public class UserMappingSqlQuery extends MappingSqlQuery<User>{ @Override protected User mapRow(ResultSet rs, int rowNum) throws SQLException { User user=new User(); user.setId((Integer)rs.getInt("id")); user.setName((String)rs.getString("name")); user.setAge((Integer)rs.getInt("age")); user.setMoney((Double)rs.getDouble("money")); return user; } } View Code
第二步,UserMappingSqlQuery類的使用。這裡重寫SpringDAODemo類中的QueryAllUser()方法。UserMappingSqlQuery需要傳入DataSource和sql,並執行compile()編譯。這裡通過getDataSource()獲取的是JdbcDaoSupport的DataSource屬性。如果有引數,可以使用setParameters()設定引數,下面的程式碼為了演示設定引數,增加了where 1=1的查詢條件。
public List<User> QueryAllUser() { String sql="select id,name,age,money from t_user where ?"; UserMappingSqlQuery userQuery=new UserMappingSqlQuery(); userQuery.setDataSource(getDataSource()); userQuery.setSql(sql); userQuery.setParameters(new SqlParameter(java.sql.Types.VARCHAR)); userQuery.compile(); return userQuery.execute(new Object[]{new String("1=1")}); } View Code
三.SqlUpdate的使用
SqlUpdate主要是用來更新,可以設定引數。SqlUpdate可以將某個功能模組化。通過下面的例子來了解下SqlUpdate的使用。
package com.demo.model; import javax.sql.DataSource; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.SqlUpdate; public class UserSqlUpdate extends SqlUpdate{ public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"}); BeanFactory factory=context; UserSqlUpdate userSqlUpdate=(UserSqlUpdate)factory.getBean("userSqlUpdate"); userSqlUpdate.updateUserMoney("小李",666.66); } public UserSqlUpdate(DataSource ds) { setDataSource(ds); setSql("update t_user set money=? where name=?"); declareParameter(new SqlParameter(java.sql.Types.DOUBLE) ); declareParameter(new SqlParameter(java.sql.Types.VARCHAR) ); compile(); } public Boolean updateUserMoney(String name,double money) { int row= update(new Object[]{new Double(money),new String(name)}); if(row>0) { System.out.println("資料新增成功!"); return true; } return false; } } View Code
在ApplicationContext.xml中配置userSqlUpdate對應的bean節點。
<bean id="userSqlUpdate" class="com.demo.model.UserSqlUpdate"> <constructor-arg ref="dataSource" index="0"></constructor-arg> </bean> View Code
在上面的UserSqlUpdate中繼承了SqlUpdate,通過setDataSource、setSql分別設定SqlUpdate的資料來源和要執行的sql。在main方法中將name為小李的money修改為666.66,執行main方法之後會打印出資料新增成功,資料庫中小李的money改成了666.66。
四.SqlFunction的使用
SqlFunction返回單一行的查詢結果,預設返回int,也可以過載返回其他型別。下面直接在main函式中使用。
package com.demo.model; import org.apache.commons.dbcp.BasicDataSource; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.object.SqlFunction; public class SqlFunctionDemo { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"}); BeanFactory factory=context; BasicDataSource dataSource=(BasicDataSource)factory.getBean("dataSource"); SqlFunction sf=new SqlFunction(dataSource,"select count(1) from t_user;"); sf.compile(); int count=sf.run(); System.out.println("User Count:"+count); } } View Code
五、事務管理
至於什麼是事務在JDBC章節已經介紹,這裡就不再說。JDBC中有事務管理,Spring使用DataSourceTransactionManager 作為JDBC的事務管理者,同時把被管理的物件使用 TransactionProxyFactoryBean配置。從名字也能猜出這裡使用的設計模式是代理設計模式。這是一個事務代理Bean,能夠使用IOC/">IOC、AOP等注入事務管理程式碼。在JDBC中介紹事務時用的轉賬操作,這裡為了更好理解,還是使用轉賬操作。
第一步,在IUserDAO介面中增加轉賬方法transfer。
public Boolean transfer(int fromUserId, int toUserId, float transferMoney);
第二步,在SpringDAODemo類中實現transfer。從fromUserId這個使用者轉賬到toUserId這個使用者,outInMoney方法就是執行sql更新資料庫使用者的money。這裡如果人為製造一個異常,把int i=1/0;這行註釋取消,就會在後面執行轉賬時自動回滾,轉賬不會成功。
public Boolean transfer(int fromUserId, int toUserId, float transferMoney) { Boolean out= outInMoney(fromUserId,-transferMoney); int i=1/0; //事務回滾 Boolean in=outInMoney(toUserId,transferMoney); return out∈ } private Boolean outInMoney(int toUserId,float money) { String sql="updatet_user set money=money+? where id=? "; int row=getJdbcTemplate().update(sql, new Object[]{money,toUserId}); if(row>0) { return true; } return false; } View Code
第三步,配置事務,在ApplicationContext.xml中配置事務管理物件DataSourceTransactionManager,設定事務代理物件TransactionProxyFactoryBean。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置業務層代理 --> <bean id="userDaoProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置目標物件 --> <property name="target" ref="userDao"/> <!-- 注入事務管理器 --> <property name="transactionManager" ref="transactionManager"/> <!-- 注入事務的屬性 --> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> View Code
第四步,執行轉賬操作,在main方法中執行transfer方法,為使用者1、2進行轉賬,如果transfer方法中人為製造的異常註釋的話是可以正常轉賬,取消註釋則轉賬失敗,列印事務回滾。
public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"}); BeanFactory factory=context; IUserDAO userDao=(IUserDAO)factory.getBean("userDaoProxy"); try { userDao.transfer(1, 2, 100); } catch(Exception e) { System.out.println("事務回滾"); } } View Code