学习目标:
1、Spring整合JDBC
2、掌握JdbcTemplate的使用
学习过程:
昨天我们讲过spring的一个特点是“不要重复发明轮子”。由于java世界里,对一些问题已经有很多非常好用的第三方技术了,spring一般不会重新开发一个,但是spring作为一个容器需要整合这些技术,所有知识简单的整合了这些技术,把他们封装到spring的容器里面,使得这些技术更加好用,所有spring能够这么快就流行起来了。这天我们就讲讲spring是如何整合JDBC和struts2这两个框架的。
一、新建项目
为了讲解今天这节课我们就先建立一个spring的数据库,然后建立一张表,一会我们将会使用spring提供的jdbc的实现操作这个表。表实现代码如下:
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`dep_id` int(11) NOT NULL AUTO_INCREMENT,
`dep_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`dep_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后,我们还需要导入相关的包,
二、spring连接数据库
我们先讲讲如何整合jdbc吧,通过这节课的学习,你就会知道spring是如何简化了对数据库的操作。要操作数据库,我们首先第一步是需要连接数据库,为了连接数据库,spring定义了几个不同的连接数据源,其中最简单的就是DriverManagerDataSource,每一次请求都会生成一个连接,所以效率不高,实现代码如下:
<!-- 建立一个连接组件 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" ></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
大家看到,非常简单,指定基本的连接信息就可以了。当然我们也可以使用SingleConnectionDataSource,它是SmartDataSource接口的一个实现,其内部包装了一个单连接。该连接在使用之后将不会关闭,很显然它不能在多线程的环境下使用。
上面两个数据源都非常适合在测试时候使用,但是如果我们需要在生产环境下使用就不适合了,因为效率都不高,我们以前学习过可以使用连接池的方式以提高效率,spring对dbcp和c3p0等连接池技术都有支持,我们也可以使用连接池的数据源,记住,spring并没有开发一个新的数据连接池,所有如果你需要使用上面的连接池技术,那么你要把相关的包先导入尽量,比使用dbcp连接池,那么你需要先把dbcp连接池所需要的包先导入尽量,然后把上面的连接组件的class实现改成dbcp就可以了,代码如下:
<!-- 建立一个连接组件 -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" ></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<!-- 你现在就可以配置连接池的信息了。-->
<property name="initialSize" value="5"></property>
<property name="maxActive" value="15"></property>
</bean>
三、使用JdbcTemplate模板类,简化数据库的操作
以前操作数据库我们都知道封装一个BaseDao类,现在你当然还可以这样做,只是spring已经帮我们封装了一个更加强大,所有这部分工作我们就可以省了。在容器中声明JdbcTemplate组件,该组件需要依赖数据源组件,所以需要注入上面定义的数据源组件,代码如下:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
然后我们就可以实现自己的业务逻辑了。一般我们建议你还是使用面向接口的方式编程,虽然这不是必须的,但是我们还是为部门操作类定义一个接口。
/**
* 对Department表操作
* @author Administrator
*
*/
public interface DepartmentDao {
public void add(Department department);
public int delete(int id);
public void update(Department department);
public Department query(int id) ;
public List<Department> queryByCondition();
public List<Department> queryByCondition(String name);
}
这节课我们先实现增收改操作,下一节我们在实现查询功能。定义一个实现了,这个实现了需要使用JdbcTemplate模板组件,这里我们使用属性注入的方式,先定义一个JdbcTemplate jdbcTemplate对象,然后在生产一个set的方法,这样才能使用属性注入,DepartmentDaoImpl实现了的代码如下:
public class DepartmentDaoImpl implements DepartmentDao {
private JdbcTemplate jdbcTemplate;
public void add(Department department) {
final String sql = "insert into department(dep_name) values(?)";
jdbcTemplate.update("insert into department(dep_name) values(?)",
new Object[] { department.getDepName() });
}
public int delete(int id) {
return jdbcTemplate.update("delete from department where dep_id=?",
new Object[] { id });
}
public void update(Department department) {
jdbcTemplate
.update("update department set dep_name=? where dep_id=?",
new Object[] { department.getDepName(),
department.getDepId() });
}
public Department query(int id) {
Department department = null;
return department;
}
public List<Department> queryByCondition() {
return null;
}
public List<Department> queryByCondition(String name) {
return null;
}
// spring属性注入
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
记住所有的组件都要交给spring容器管理,所有你先不要写个main方法,然后通过我们以前new一个dao对象调用方法,现在我们需要在spring的容器中定义这个组件。
<!--spring 整合jdbc 部门dao -->
<bean id="departmentDao" class="com.dao.impl.DepartmentDaoImpl" >
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
并注入jdbctemplate组件。然后我们在写一个main方法测试是否能正常操作数据库。
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext(
"src/springContext.xml");
DepartmentDao departmentDao = (DepartmentDao) context
.getBean("departmentDao");
Department department = new Department();
department.setDepName("test");
departmentDao.add(department);
}
运行上面的代码,如果没有问题就应该插入成功了。可见这个jdbcTemplate和我们之前封装的BaseDao的使用差不多,事实这个jdbcTemplate还有很多高级的使用方法。下面我们就简单讲解一下。
一、获得用户刚刚插入数据的id
这个问题我们以前做项目的时候碰到过,不同的数据的自增方式是不同的,所有要去的刚刚插入的数据库的那条数据的id不同的数据库也有不同实现方法,但是jdbcTemplate已经帮我们封装好了。所有我们可以很方便的获得这个值,修改add方法,当插入完成后可以获得刚刚插入这条数据在数据库中对应的id。只需要使用KeyHolder对象就可以了,实现代码如下:
public void add(final Department department) {
final String sql="insert into department(dep_name) values(?)";
/* jdbcTemplate.update("insert into department(dep_name) values(?)",
new Object[] { department.getDepName() });*/
KeyHolder keyHolder=new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection arg0)
throws SQLException {
PreparedStatement ps=jdbcTemplate.getDataSource().getConnection().prepareStatement(sql);
ps.setString(1, department.getDepName());
return ps;
}
},keyHolder);
//重新set进去
department.setDepId(keyHolder.getKey().intValue());
}
再次测试上面的代码
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext(
"src/springContext.xml");
DepartmentDao departmentDao = (DepartmentDao) context
.getBean("departmentDao");
Department department = new Department();
department.setDepName("test2");
departmentDao.add(department);
System.out.println("数据库对应生成的id:"+department.getDepId());
}
二、查询操作
下面我们研究一下查询操作,原始的jdbc查询是返回的是ResultSet对象,然后把数据库的字段值逐个set到javaBean对象的属性中。jdbcTemplate也是差不多,只是它返回的是一个Map对象,map的key对应的就是字段名称,下面我们实现根据id进行查询的操作,代码如下:
public Department query(int id) {
Department department = null;
// 如果不使用RowMapper,那么就像以前ResultSet一样要一个个的set进去了。
List<Map> maps = jdbcTemplate.queryForList(
"select * from department where dep_id=?", new Object[] { id });
if (maps != null && maps.size() > 0) {
Map<Object, Object> map = maps.get(0);
department = new Department();
department.setDepId((Integer) map.get("dep_id"));
department.setDepName(map.get("dep_name").toString());
}
return department;
}
记得以前我们封装的高级jdbc把,那时候我们通过反射的方式写了一个自动ResultSet到javaBean对象的转换,其实jdbcTemplate也有类似的实现,只需要你实现RowMapper接口就可以了,我们使用内部类的方式,代码如下:
class DepartmentMap implements RowMapper{
public Object mapRow(ResultSet rs, int arg1) throws SQLException {
Department department=new Department();
department.setDepId(rs.getInt("dep_id"));
department.setDepName(rs.getString("dep_name"));
return department;
}
}
实现查询全部和根据名称查询两个方法,只需要传入DepartmentMap对象就可以了。代码如下:
public List<Department> queryByCondition() {
List<Department> departments = jdbcTemplate.query("select * from department", new DepartmentMap());
return departments;
}
public List<Department> queryByCondition(String name) {
List<Department> departments = jdbcTemplate.query("select * from department where dep_name like '%"+name+"%'", new DepartmentMap());
return departments;
}
你可以在main方法中测试一下:
List<Department> departments=departmentDao.queryByCondition("t");
for(Department department2:departments){
System.out.println(department2.getDepName());
}