该系列为imooc Spring从入门到进阶笔记,跟随课程加入自己见解,同时也为项目中碰到一些问题做了解答
本门课程是使用Spring组件JDBC Template简化持久化操作
大纲
1、课程介绍
课程目标
- 了解Spring组件JDBC Template
- 能使用JDBC Template进行 持久化操作
- 帮助自己学习Hibernate、 MyBatis等ORM框架
前置条件
- JDBC
- Spring IOC、Spring AOP
- Mysql
课程中老师演示项目的环境
- 操作系统:Win7
- 数据库:MySQL5.7
- JDK:jdk1.8.0
2、JDBC Template概念
Java访问数据库传统形式 与 通过JDBC Template访问数据库形式的对比
JDBC Template提供统一的模板方法,在保留代码灵活性的基础上, 尽量减少持久化代码
3、案例准备
项目示例:学生选课系统,包含三张表,表结构如下
- 准备数据库
数据库表结构
建表语句
drop database if exists selection_course;
create database selection_course;
use selection_course;
create table course
(
id int not null auto_increment,
name char(20),
score int,
primary key (id)
);
create table selection
(
student int not null,
course int not null,
selection_time datetime,
score int,
primary key (student, course)
);
create table student
(
id int not null auto_increment,
name varchar(20),
sex char(2),
born date,
primary key (id)
);
alter table selection add constraint FK_Reference_1 foreign key (course)
references course (id) on delete restrict on update restrict;
alter table selection add constraint FK_Reference_2 foreign key (student)
references student (id) on delete restrict on update restrict;
insert into course(id,name,score) values(1001,'英语',5);
insert into course(id,name,score) values(1002,'操作系统',5);
insert into course(id,name,score) values(1003,'数据结构',3);
commit;
- 创建项目
- 配置项目POM文件
<properties>
<spring.version>4.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</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-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
- 配置Spring applicationContext.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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.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/selection_course?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
4、JDBC Template基本使用及持久化案例
JDBC Template作为一个工具类,给使用者提供的简化持久化操作的功能是通过方法来呈现的,方法分为四类
- execute方法
- update与batchUpdate方法
- query与queryXXX方法
- call方法
4.1、execute方法
execute方法,一般用来创建数据表
使用流程
- 获取Bean中的JdbcTemplate对象
- 调用jdbcTemplate.execute(String sql)方法执行sql语句
代码演示
在test>java文件夹下>新建Test类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class Test {
private JdbcTemplate jdbcTemplate;
{
//1、获取JDBC Template对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate=(JdbcTemplate)applicationContext.getBean("jdbcTemplate");
}
@org.junit.Test
//execute方法
public void testExecute(){
//2、通过调用execute()方法执行DDL语句
jdbcTemplate.execute("create table user1(id int,name varchar (20))");
}
}
启动测试
可见execute方法执行成功,新增了user1表
4.2、update方法和batchUpdate方法
- update方法 ,对数据进行增删改操作
int update(String sql, Object[] args)
int update(String sql, Object… args)
- batchUpdate方法,批量增删改操作
int[] batchUpdate(String[] sql) //一次执行多条sql,每条sql需要写明确
int[] batchUpdate(String sql, List<Object[]> args)//多次执行一条相同的sql,每次执行的参数放在list中
返回值为影响的行数
代码演示
4.2.1、演示int update(String sql,Object[] args)方法
在test>java文件夹下>Test类中>新增testUpdate方法
//演示update(String sql, Object[] args) 方法 ,对数据进行增删改操作
@org.junit.Test
public void testUpdate(){
String sql="insert into student(name,sex) values(?,?)";
jdbcTemplate.update(sql,new Object[]{"张飞","男"});
}
启动测试
可见update(String sql, Object[] args) 方法执行成功
4.2.2、演示int update(String sql,Object… args)方法
在test>java文件夹下>Test类中>新增testUpdate2方法
//演示update(String sql, Object… args)方法 ,对数据进行增删改操作
@org.junit.Test
public void testUpdate2(){
String sql="update student set sex=? where id=?";
jdbcTemplate.update(sql,"女",1);
}
启动测试
可见update(String sql, Object… args)执行成功
4.2.3、演示int[] batchUpdate(String[] sql)方法
在test>java文件夹下>Test类中>新增testBatchUpdate方法
@org.junit.Test
//演示batchUpdate(String[] sql) ,对数据批量操作
public void testBatchUpdate(){
String[] sqls={
"insert into student(name,sex) values('关羽','女')",
"insert into student(name,sex) values('刘备','男')",
"update student set sex='男' where id=1"
};
jdbcTemplate.batchUpdate(sqls);
}
启动测试
可见 batchUpdate(String[] sql) 执行成功
4.2.4、演示int[] batchUpdate(String sql, List<Object[]
> args)方法
在test>java文件夹下>Test类中>新增testBatchUpdate2方法
@org.junit.Test
//演示batchUpdate(String sql, List<Object[]> args) ,对数据批量操作
public void testBatchUpdate2(){
String sql = "insert into selection(student,course) values(?,?)";
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[]{1,1001});
list.add(new Object[]{1,1003});
jdbcTemplate.batchUpdate(sql,list);
}
启动测试
可见batchUpdate(String sql, List<Object[]> args)执行成功
4.3、query方法与queryXXX方法
4.3.1、查询简单数据项
- 查询结果为单条记录,查询SQL的结果必须是单行,不然会报错
T queryForObject(String sql, Class<T> type)
T queryForObject(String sql, Object[] args, Class<T> type)
T queryForObject(String sql, Class<T> type,Object… arg)
- 查询结果为多条记录 ,查询SQL的结果必须与参数
Class<T> type
类型相同,不然会报错
List<T> queryForList(String sql , Class<T> type)
List<T> queryForList(String sql , Object[] args , Class<T> type)
List<T> queryForList(String sql , Class<T> type,Object… arg)
4.3.1.1、演示T queryForObject(String sql,Class<T
> type)方法
在test>java文件夹下>Test类中>新增testQuerySimple1方法
@org.junit.Test
//演示queryForObject(String sql, Class<T> type)方法
public void testQuerySimple1(){
String sql = "select count(*) from student";
int count = jdbcTemplate.queryForObject(sql,Integer.class);
System.out.println(count);
}
我们先看看student表中有多少数据
启动测试
可见queryForObject(String sql, Class type)方法执行成功
4.3.1.2、演示List<T
> queryForList(String sql,Class<T
> type,Object… arg)方法
在test>java文件夹下>Test类中>新增testQuerySimple2方法
@org.junit.Test
//演示queryForList(String sql , Class<T> type,Object… arg)方法
public void testQuerySimple2(){
String sql = "select name from student where sex=?";
List<String> names = jdbcTemplate.queryForList(sql,String.class,"男");
System.out.println(names);
}
启动测试
可见queryForList(String sql , Class type,Object… arg)方法执行成功
4.3.2、查询复杂对象(封装为Map)
- 获取一个
Map queryForMap(String sql)
Map queryForMap(String sql , Object[] args)
Map queryForMap(String sql , Object… arg)
- 获取多个
List<Map<String,Object>> queryForList(String sql)
List<Map<String,Object>> queryForList(String sql , Object[] args)
List<Map<String,Object>> queryForList(String sql , Object… arg)
4.3.2.1、演示Map queryForMap(String sql,Object… arg)方法
在test>java文件夹下>Test类中>新增testQueryMap1方法
@org.junit.Test
//演示Map queryForMap(String sql , Object… arg) 方法
public void testQueryMap1(){
String sql = "select * from student where id = ?";
Map<String,Object> stu = jdbcTemplate.queryForMap(sql,1);
System.out.println(stu);
}
启动测试
可见queryForMap(String sql , Object… arg) 执行成功
4.3.2.2、演示List<
Map<
String,Object>>
queryForList(String sql) 方法
在test>java文件夹下>Test类中>新增testQueryMap2方法
@org.junit.Test
//演示M演示List<Map<String,Object>> queryForList(String sql)方法方法
public void testQueryMap2(){
String sql = "select * from student";
List<Map<String,Object>> stus = jdbcTemplate.queryForList(sql);
System.out.println(stus);
}
启动测试
可见List<Map<String,Object>> queryForList(String sql)执行成功
4.3.3、查询复杂对象(查询出来的结果封装为实体对象)
RowMapper接口
Spring JdbcTemplate中关于RowMapper的使用实例
项目中数据库存在student表,表中每一条记录代理一个学生;而在Java代码中,定义一个Student类,一个Student类对象表示一个学生。我们希望持久化操作时,从student表中查询出来的记录,能够方便的转化成Student类对象。但是在转化过程中,student表中字段怎么与Student类属性一一对应?JDBC Template在持久化操作时,就是通过RowMapper接口,实现表中字段与类属性一一对应的映射关系
我们可以通过new RowMapper<对象>()将数据库中获取的数据封装成我们需要的对象类型。实现接口中的mapRow(ResultSet r, int i)方法,在这个方法体中,通过结果集ResultSet获取数据库各字段的数据,然后在封装就可以了。
- 获取一个
T queryForObject(String sql,RowMapper<T> mapper)
T queryForObject(String sql,Object[] args,RowMapper<T> mapper)
T queryForObject(String sql,RowMapper<T> mapper,Object… arg)
- 获取多个
List<T> query(String sql,RowMapper<T> mapper)
List<T> query(String sql,Object[] args,RowMapper<T> mapper)
List<T> query(String sql,RowMapper<T> mapper,Object… arg)
使用前的准备
- 以上queryForObject方法与query方法中,需要RowMapper接口实例化对象作为参数,那我们在Test类中声明私有的StudentRowMapper 类来实现RowMapper接口
private class StudentRowMapper implements RowMapper<Student> {
public Student mapRow(ResultSet resultSet, int i) throws SQLException {
Student stu = new Student();
stu.setId(resultSet.getInt("id"));
stu.setName(resultSet.getString("name"));
stu.setSex(resultSet.getString("sex"));
stu.setBorn(resultSet.getDate("born"));
return stu;
}
}
- 在Java>新建com.imooc.sc.entity包>新建Student 类(学生实体类)
package com.imooc.sc.entity;
import java.util.Date;
public class Student {
private int id;
private String name;
private String sex;
private Date born;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBorn() {
return born;
}
public void setBorn(Date born) {
this.born = born;
}
public String toString(){
return "Student{"+id+","+name+","+sex+","+born+"}";
}
}
4.3.3.1、演示T queryForObject(String sql,RowMapper<T
> mapper,Object… arg)方法
在test>java文件夹下>Test类中>新增testQueryEntity1方法
@org.junit.Test
//演示T queryForObject(String sql,RowMapper<T> mapper,Object… arg)
public void testQueryEntity1(){
String sql = "select * from student where id = ?";
Student stu = jdbcTemplate.queryForObject(sql, new StudentRowMapper(), 1);
System.out.println(stu);
}
启动测试
可见T queryForObject(String sql,RowMapper<T
> mapper,Object… arg)执行成功
4.3.3.2、演示List<T
> query(String sql,RowMapper<T
> mapper)方法
在test>java文件夹下>Test类中>新增testQueryEntity2方法
@org.junit.Test
//演示List<T> query(String sql,RowMapper<T> mapper)
public void testQueryEntity2(){
String sql = "select * from student";
List<Student> stus = jdbcTemplate.query(sql,new StudentRowMapper());
System.out.println(stus);
}
启动测试
可见List<T
> query(String sql,RowMapper<T
> mapper)方法执行成功
5、在项目中,使用JDBC Template封装持久层,结构上应该怎么设计?
在第3节中,我们已知示例为学生选课系统,包含三张表(学生表、课程表、选课表),在4.3.3中我们在com.imooc.sc.entity包中创捷了Student类,下面我们继续完善实体类
- 在Java>com.imooc.sc.entity包>新建Course类(课程实体类)
package com.imooc.sc.entity;
public class Course {
private int id;
private String name;
private int score;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
- 在Java>com.imooc.sc.entity包>新建Selection类(选课记录实体类)
package com.imooc.sc.entity;
import java.util.Date;
public class Selection {
private int sid;
private int cid;
private Date selTime;
private int score;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public int getCid() {
return cid;
}
public void setCid(int cid) {
this.cid = cid;
}
public Date getSelTime() {
return selTime;
}
public void setSelTime(Date selTime) {
this.selTime = selTime;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
接下来完善持久层接口与持久层接口实现类(在该项目示例中没有业务层去调用持久层,故不需要接口进行关联,但是为了严谨起见,还是创建持久层接口)
在持久层接口实现类中需要
- 注入jdbcTemplate
- 声明RowMapper(不需要查询返回实体类时,则不需声明)
- 在Java>新建com.imooc.sc.dao包>新建StudentDao 接口(StudentDao 持久层接口)
package com.imooc.sc.dao;
import com.imooc.sc.entity.Student;
import java.util.List;
public interface StudentDao {
void insert(Student stu);
void update(Student stu);
void delete(int id);
Student select(int id);
List<Student> selectAll();
}
- 在Java>新建com.imooc.sc.dao.impl包>新建StudentDaoImpl 类(StudentDao 持久层接口实现类)
package com.imooc.sc.dao.impl;
import com.imooc.sc.dao.StudentDao;
import com.imooc.sc.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class StudentDaoImpl implements StudentDao {
//注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(Student stu) {
String sql = "insert into student(name,sex,born) values(?,?,?)";//born需要的参数类型是sql.DATE
jdbcTemplate.update(sql,stu.getName(),stu.getSex(),stu.getBorn());//stu.getBorn()返回的参数类型是util.Date,按理说“stu.getBorn()”需要进行类型转换,但是jdbcTemplate已经帮我们自动转换了
}
public void update(Student stu) {
String sql = "update student set name=?,sex=?,born=? where id=?";
jdbcTemplate.update(sql,stu.getName(),stu.getSex(),stu.getBorn(),stu.getId());
}
public void delete(int id) {
String sql = "delete from student where id=?";
jdbcTemplate.update(sql,id);
}
public Student select(int id) {
String sql = "select * from student where id=?";
return jdbcTemplate.queryForObject(sql,new StudentRowMapper(),id);
}
public List<Student> selectAll() {
String sql = "select * from student";
return jdbcTemplate.query(sql,new StudentRowMapper());
}
//声明私有的StudentRowMapper 类来实现RowMapper接口
private class StudentRowMapper implements RowMapper<Student> {
public Student mapRow(ResultSet resultSet, int i) throws SQLException {
Student stu = new Student();
stu.setId(resultSet.getInt("id"));
stu.setName(resultSet.getString("name"));
stu.setSex(resultSet.getString("sex"));
stu.setBorn(resultSet.getDate("born"));
return stu;
}
}
}
- 在Java>com.imooc.sc.dao包>新建CourseDao 接口(CourseDao 持久层接口)
package com.imooc.sc.dao;
import com.imooc.sc.entity.Course;
import java.util.List;
public interface CourseDao {
void insert(Course course);
void update(Course course);
void delete(int id);
Course select(int id);
List<Course> selectAll();
}
- 在Java>com.imooc.sc.dao.impl包>新建CourseDaoImpl类(CourseDao 持久层接口实现类)
package com.imooc.sc.dao.impl;
import com.imooc.sc.dao.CourseDao;
import com.imooc.sc.entity.Course;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class CourseDaoImpl implements CourseDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(Course course) {
String sql = "insert into course(name,score) values(?,?)";
jdbcTemplate.update(sql,course.getName(),course.getScore());
}
public void update(Course course) {
String sql = "update course set name=?,score=? where id=?";
jdbcTemplate.update(sql,course.getName(),course.getScore(),course.getId());
}
public void delete(int id) {
String sql = "delete from course where id=?";
jdbcTemplate.update(sql,id);
}
public Course select(int id) {
String sql = "select * from course where id=?";
return jdbcTemplate.queryForObject(sql,new CourseRowMapper(),id);
}
public List<Course> selectAll() {
String sql = "select * from course";
return jdbcTemplate.query(sql,new CourseRowMapper());
}
private class CourseRowMapper implements RowMapper<Course> {
public Course mapRow(ResultSet resultSet, int i) throws SQLException {
Course course = new Course();
course.setId(resultSet.getInt("id"));
course.setName(resultSet.getString("name"));
course.setScore(resultSet.getInt("score"));
return course;
}
}
}
- 在Java>com.imooc.sc.dao包>新建SelectionDao 接口(SelectionDao 持久层接口)
package com.imooc.sc.dao;
import com.imooc.sc.entity.Selection;
import java.util.List;
import java.util.Map;
public interface SelectionDao {
void insert(List<Selection> seles);
void delete(int sid,int cid);
List<Map<String,Object>> selectByStudent(int sid);
List<Map<String,Object>> selectByCourse(int cid);
}
- 在Java>com.imooc.sc.dao.impl包>新建SelectionDaoImpl 类(SelectionDao 持久层接口实现类)
package com.imooc.sc.dao.impl;
import com.imooc.sc.dao.SelectionDao;
import com.imooc.sc.entity.Selection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Repository
public class SelectionDaoImpl implements SelectionDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(List<Selection> seles) {
String sql = "insert into selection values(?,?,?,?)";
List<Object[]> list = new ArrayList<Object[]>();
for(Selection sel:seles){
Object[] args = new Object[4];
args[0] = sel.getSid();
args[1]=sel.getCid();
args[2] = sel.getSelTime();
args[3] =sel.getScore();
list.add(args);
}
jdbcTemplate.batchUpdate(sql,list);
}
public void delete(int sid,int cid) {
String sql = "delete from selection where student=? and course=?";
jdbcTemplate.update(sql,sid,cid);
}
public List<Map<String, Object>> selectByStudent(int sid) {
String sql = "select se.*,stu.name sname,cou.name cname from selection se " +
"left join student stu on se.student=stu.id " +
"left join course cou on se.course=cou.id" +
"where student=?";
return jdbcTemplate.queryForList(sql,sid);
}
public List<Map<String, Object>> selectByCourse(int cid) {
String sql = "select se.*,stu.name sname,cou.name cname from selection se " +
"left join student stu on se.student=stu.id " +
"left join course cou on se.course=cou.id" +
"where course=?";
return jdbcTemplate.queryForList(sql,cid);
}
}
- 在Spring配置文件applicationContext中配置注解自动扫面
<context:component-scan base-package="com.imooc.sc"/>
6、JDBC Template优缺点分析
优点:
- 简单
- 灵活
缺点:
- SQL与Java代码参杂
- 功能不丰富