持久层:所谓“持久层”,也就是在系统逻辑层面上,专著于实现数据持久化的一个相对独立的领域(Domain),是把数据保存到可掉电式存储设备中。持久层是负责向(或者从)一个或者多个数据存储器中存储(或者获取)数据的一组类和组件。大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
一、DAO的支持
DAO是数据访问对象(data access object)的缩写,是一个面向对象的数据库接口。对于数据库的存取,spring提供了DAO的支持,可以不用管任何
的底层数据库细节,spring 的一组DAO接口就可以完成所有数据的操作。DAO提供了数据读取和写入到数据库中的一种方式。
spring没有自己的持久层实现,但是它提供了DAO的支持,可以任意封装任何其他的持久层实现框架。
服务对象通过接口来访问DAO,优点:
1.使得服务对象易于测试,因为他们不再与特定的数据访问实现绑定在一起。
2.数据访问层是以持久化技术无关的方式进行访问的。持久化方式的选择独立于DAO,只有相关的数据访问方法通过接口来进行发布。这样可以实现灵活的设计并使得切换持久化框架对应用程序其他部分所带来的影响最小。
spring将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template)和回调(callback)。
spring的模板类处理数据访问的固定部分——事物控制、管理资源以及处理异常。
spring的回调类处理应用程序相关的数据访问——创建语句、绑定参数以及整理结果集。
(1)举一个传统的DAO例子,首先建立一个用户对象User
package spring.chapter4; public class User { private Long id; private String username; private String password; public Long getId() { return id; } public void setId(Long id) { this.id=id; } public String getUsername() { return username; } public void setUsername(String username) { this.username=username; } public String getPassword() { return password; } public void setPassword(String password) { this.password=password; } }
(2)设计一个操作用户的DAO接口IUserDAO
package spring.chapter4; public interface IUserDAO { public void insert(User user); public void delete(User user); public void update(User user); public User find(Long id); }
(3)写一个IUserDAO的实现类,就是操作数据库的底层代码
package spring.chapter4; import java.sql.SQLException; import javax.activation.DataSource; import com.mysql.jdbc.Connection; import com.mysql.jdbc.PreparedStatement; import com.mysql.jdbc.StatementImpl; public class UserDAO implements IUserDAO { private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource=dataSource; } public void insert(User user) { String userName=user.getUsername(); String password=user.getPassword(); Connection conn=null; java.sql.PreparedStatement stmt=null; try { conn=dataSource.getConnection(); stmt=conn.prepareStatement("insert into user(name,password) value(?,?)"); stmt.setString(1, userName); stmt.setString(2, password); }catch(SQLException e) { e.printStackTrace(); } finally { if(stmt!=null) { try { stmt.close(); }catch(SQLException e) { e.printStackTrace(); } } if(conn!=null) { try { conn.close(); }catch(SQLException e) { e.printStackTrace(); } } } } //省略其他方法 }
二、数据源的注入
1.在spring中都是采用IOC(控制反转)方式注入数据源,直接在配置文件中给DAO注入数据源。在spring中不用考虑数据库的类型,均可以使用配置文件来完成数据源的注入,同时支持多种不同的数据源,如JDBC、连接池和JNDI等注入。不管使用声明方式,只要保留一个DataSource的接口就可以进行数据源注入了。
(4)结合前面的UserDAO,可以写如下配置文件来注入DataSource。(通过jdbc驱动程序定义的数据源)
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/news" /> <property name="username" value="root" /> <property name="password" value="******" /> </bean> <bean id="userDao" class="spring.chapter4.UserDAO"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
(5)测试代码如下:
package spring.chapter4; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestDAO { public static void main(String [] args) { ApplicationContext context=new ClassPathXmlApplicationContext("spring/chapter4/web.xml"); User user=new User(); user.setUsername("gaochao"); user.setPassword("gaochao"); IUserDAO userDao=(IUserDAO)context.getBean("userDao"); userDao.insert(user); System.out.println("name:"+user.getUsername()+",password:"+user.getPassword()); } }
2.DBCP连接池注入数据源
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/news" /> <property name="username" value="root" /> <property name="password" value="******" /> </bean> <bean id="userDao" class="spring.chapter4.UserDAO"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
可以看出,只需要修改Bean的class为org.apache.commons.dbcp.BasicDataSource就可以。注意这里设置了一个destroy-method方法,该方法表示在BeanFactory关闭的时候调用close方法关闭BasicDataSource。
3.JNDI的支持
与DBCP相比,在进行web开发中,更多的是使用JNDI来连接数据库。这里以Tomcat容器为例,首先在Tomcatconf目录下找到server.xml中的</Host>前添加context,代码如下:
<!-应用程序的目录是webapp --> <Context path="webapp" docBase="webapp"> <Resource name="jdbc/news" scope="Shareable" type="javax.sql.DataSource" /> <ResourceParams name="jdbc/news"> <parameter> <name>factory</name> <value>org.apache.commons.dbcp.BasicDataSourceFactory</value> </parameter> <!-数据库的连接URL --> <parameter> <name>url</name> <value>jdbc:mysql://localhost:3306/news</value> </parameter> <!- 数据库连接的驱动程序 --> <parameter> <name>driverClassName</name> <value>com.mysql.jdbc.Driver</value> </parameter> <!-数据库连接的用户名 --> <parameter> <name>username</name> <value>gaochao</value> </parameter> <!-数据库连接的密码 --> <parameter> <name>password</name> <value>*****</value> </parameter> <!-等待连接的时间,单位为毫秒,-1为不限制 --> <parameter> <name>maxWait</name> <value>3000</value> </parameter> <!-连接池中最少连接数,0为不限制 --> <parameter> <name>maxIdle</name> <value>10</value> </parameter> <!-连接池中最多的连接数,0为不限制 --> <parameter> <name>maxActive</name> <value>100</value> </parameter> </ResourceParams> </Context>
在Tomcat配置好连接池后就可以修改前面的spring配置文件来获取连接池:
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="jndiName" value="jdbc/news" /> </bean> <bean id="userDao" class="spring.chapter4.UserDAO"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
三、JDBC的支持
1.Template模式:在父类中定义一个操作中的骨架算法,而将一些用户自定义的逻辑延迟到子类中。示例:加入所有的business方法中都必须进行用户验证、异常捕获和一个业务操作,那么就将用户验证、异常捕获封装到父类中,子类只需要写一些business代码就可以了。父类代码如下:
package spring.chapter4; public abstract class BusinessTemplate { public void execute() throws Exception{ validataUser(); try { business(); doBusiness(); }catch (Error e) { e.printStackTrace(); } } void validataUser() { System.out.println("验证用户..."); }; void business() { System.out.println("业务1"); } public abstract void doBusiness(); }
这里是一个抽象类,有一个execute()方法,在该方法中进行了用户验证、异常捕获和一个业务的执行。在这三项任务结束后执行了doBusiness()方法,同时doBusiness()方法又是一个抽象方法,继承该抽象类就要重写这个抽象方法,在这里定义用户自己的业务,写一个子类如下:
package spring.chapter4; public class Business extends BusinessTemplate{ public void doBusiness() { System.out.println("自定义业务"); } }
这里继承类就重写了doBusiness方法,定义一个用户自己的业务,接着写一个测试类如下:
package spring.chapter4; public class TestTemplate { public static void main(String[] args) throws Exception { Business business=new Business(); business.execute(); } }
这里的Business类调用了父类的execute方法,按execute()方法执行顺序来执行后,运行结果如下:
这里的Business类是BusinessTemplate的一个子类,原本也需要写用户验证和异常捕获等业务,而采用了一个父类模板就将所有的业务都省略了,其他任何想乣进行用户验证。异常捕获和业务这些操作的逻辑都只需要继承BusinessTemplate,然后写用户的逻辑代码就可以了,再也不用写那些重复的逻辑了,spring正是采用了这样的方法来封装了数据库操作的Connection,Statement这些连接、关闭及异常捕获等。
使用JDBC模板:
▲JdbcTemplate:最基本的spring JDBC模板,这个模板支持最简单的JDBC数据库访问功能以及简单的索引参数查询
▲NamedParameterJdbcTemplate:使用该模板类执行查询时,可以将查询值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数
▲SimpleJdbcTemplate:该模板利用java 5的一些特性,如自动装箱,泛型以及可变参数列表来简化JDBC模板的使用
2.JdbcTemplate
spring提高了org.springframework.jdbc.core.JdbcTemplate类,它封装了数据库的操作的方法。JdbcTemplate的构造函数接受一个DataSource对象,使用JdbcTemplate template=new JdbcTemplate(dataSource)来完成JdbcTemplate的初始化,然后就可以使用JdbcTemplate来操作数据库了。将UserDAO使用JdbcTemplate为:
package spring.chapter4; import java.awt.List; import java.util.Iterator; import java.util.Map; import javax.activation.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class UserDAO1 implements IUserDAO { private JdbcTemplate jdbctemplate; public void setDataSource(DataSource dataSource) { jdbctemplate = new JdbcTemplate(dataSource); } public void insert(User user) { String name=user.getUsername(); String password=user.getPassword(); this.jdbctemplate.update("insert into user (username,password)"+"values('"+name+"','"+password+"')"); } public User find(Long id) { List list=jdbctemplate.queryForList("select * form user where id="+id.longValue()); Iterator it=list.iterator(); if(it.hasNext()) { Map map=(Map) it.next(); Long l=new Long(map.get("id").toString()); String name=map.get("username").toString(); String password=map.get("password").toString(); User user=new User(); user.setId(l); user.setUsername(name); user.setPassword(password); return user; } return null; } //省略其他方法 }
2.1使用JdbcTemplate查询数据库
(1)返回单个对象查询:
Object query(String sql,ResultSetExtractor extractor)
Object query (String sql,Object[] args,ResultSetExtractor extractor)
int queryForInt(String sql)
int queryForInt(String sql,Object[] args)
int queryForLong(String sql)
long queryForLong(String sql,Object[] args)
Object queryForObject(String sql,Class requiredType)
Object queryForObject(String sql,Object[] args,Class requiredType)
Object queryForObject(String sql,RowMapper rowMapper)
Object queryForObject(String sql,Object[] args,RowMapper rowMapper)
(2)返回多个对象查询:
List query(String sql,RowMapper rowMapper)
List query(String sql,Object[] args,RowMapper rowMapper)
List queryForList(String sql)
List queryForList(String sql,Object[] args)
List queryForList(String,Class elementType)
2.2使用JdbcTemplate更新数据
void execute(String sql)
Object execute(String sql,PrepareStatementCallback action)
Object execute(ConnectionCallback action)
int update(String sql)
int update(String sql,Object[] args)
int update(PrepareStatementCreator psc)
int update(String sql,PreparestatementSetter pss)
int update(String sql,Object[] args,int[] argTypes)
2.3面向对象查询数据
spring JDBC的操作还是使用了sql语句,如果对sql不是非常熟悉的程序员可能在运用上有麻烦,为此spring提供了org.springframework.jdbc.object包来设计完全面向对象查询,只要封装一个面向对象的查询类,丝毫不用写任何sql语句就可以完成JdbcTemplate所有的数据库操作功能。
(1)org.springframework.jdbc.object.SqlQuery
(2)org.springframework.jdbc.object.MappingSqlQuery
(3)org.springframework.jdbc.object.SqlUpdate
(4)org.springframework.jdbc.object.SqlFunction
3.NameParameterJdbcTemplate:
4.SimpleJdbcTemplate: