Spring对JDBC的支持
JDBC是连接数据库的标准,DBUtil或JdbcTemplete工具都是在原生JDBC基础上封装而来,
一切进行数据操作的工具或框架都是JDBC基础上封装而来。
在以后的项目开发中,我们一般不会使用原生的JDBC进行数据库操作,也不会使用DBUtil这种类似的工具,
而我们今天讲解的JdbcTemplate也属于工具,不是框架。我们讲解JdbcTemplate目的就是为了讲解后面的声明式事务,我们以后的开发肯定使用框架开发,目前主流的ORM【Object relation mapping】对象关系映射框架
就是MyBatis(半自动化框架),往前就是Hibernate(全自动化框架)。
事务处理是放在业务层,数据增删改查操作是在数据持久化访问层。
JdbcTemplate
为了使JDBC的操作更加简洁,易于使用,Spring在原生的JDBC的API上定义了一个抽象层,
从而建立了一个JDBC的存取的工具。这个工具是作为SpringJDBC的核心,创建的一系列增删改查操作模板,
每个模板方法都能够控制整个过程,并允许覆盖过程中的特定任务,
以这种方式可以在尽可能保留灵活性的情况下,将数据库的增删改查的工作量降到最低。
JDBCTemplate的操作步骤:
1.加入jar包
c3p0-0.9.1.2.jar
commons-logging-1.1.3.jar
mysql-connector-java-5.1.37-bin.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
2.配置数据库连接信息:conf类路径配置文件下的db.properties文件(普通的file文件)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/buybook
user=root
password=3306
maxSize=10 //连接池最大值
initSize=5 //连接池初始化值
3.配置Spring的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"
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-4.0.xsd">
<!-- 导入外部的属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
//不是springEL表达式,是jspEL表达式
<property name="driverClass" value="${driver}"></property>
<property name="jdbcUrl" value="${url}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="maxPoolSize" value="${maxSize}"></property>
<property name="initialPoolSize" value="${initSize}"></property>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
4.编写测试类
package com.wanbangee.junit;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class Test {
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = act.getBean(JdbcTemplate.class);
String sql = "insert into account(acc_name,acc_pass,acc_balance) values(?,?,?),(?,?,?)";
int x = jdbcTemplate.update(sql, "aa","123456",50,"bb","223456",500);
//执行增删改操作,并传递sql中?号占位符的值,返回值表示影响的数据笔数
System.out.println(x);
}
}
JUnit Test Case
使用Junit,和new类一样new个JUnit Test Case,并命名(和类名命名一样),勾上setUp()和tearDown()
package com.wanbangee.junit;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class JunitDemo01 {
//在每个@Test注解的测试方法执行之前都会执行setUp方法
@Before
public void setUp() throws Exception {
System.out.println("before,在所有的测试方法执行之前执行");
}
//每个@Test注解的测试方法执行之后都会执行tearDown方法
@After
public void tearDown() throws Exception {
System.out.println("after,在所有的测试方法执行之后执行");
}
//在此页面右击此处@Test的方法,run as采用JUnit Test方式运行,运行结果有一长串绿色表示运行成功
@Test
public void test() {
System.out.println("Hello Junit,测试方法");
}
//运行结果有一长串红色表示运行不成功,有异常或错误
@Test
public void test01() {
System.out.println(1/0);
System.out.println("Hello test01,测试方法");
}
}
Junit 是标准的单元测试工具
使用Junit(添加Junit步骤:右击项目名,点properties,java build path,添加Libraries,Add Library,
选最新的JUnit4版本)新建个包,在包下new个JUnit Test Case,随便取个名字(和类名命名一样),
勾上setUp()和tearDown()
package com.wanbangee.junit;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcTemplateTest {
ConfigurableApplicationContext act = null;
JdbcTemplate jdbcTemplate = null;
@Before
public void setUp() throws Exception {
act = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = act.getBean(JdbcTemplate.class);
}
@After
public void tearDown() throws Exception {
act.close();
}
//update方法执行增删改操作,并传递sql中?号占位符的值,返回值为int型,表示影响的数据笔数。
@Test
public void testDelete() {
String sql = "delete from account where acc_id = ?";
jdbcTemplate.update(sql, 21);
}
@Test
public void testUpdate() {
String sql = "update account set acc_name = ? where acc_id = ?";
jdbcTemplate.update(sql, "张三丰",19);
}
@Test
public void testInsert() {
String sql = "insert into account(acc_name,acc_pass,acc_balance) values(?,?,?)";
jdbcTemplate.update(sql, "张三丰","123456",500);
}
//查询单笔数据
@Test
public void testQueryOneLine() {
String sql = "select acc_id,acc_name,acc_pass,acc_balance,acc_pic from account where acc_id = ?";
RowMapper<Account> rowMapper = null;
//会将查询结果集的行映射成实体类的一个对象
rowMapper = new BeanPropertyRowMapper<>(Account.class);
//会根据查询的列名和实体类Javabean风格的属性名自动匹配,列名如果带下划线,则会匹配对应驼峰的属性
Account account = jdbcTemplate.queryForObject(sql, rowMapper, 11);//11表示id为11的
System.out.println(account);
}
//查询多笔数据
@Test
public void testQueryAnyLine() {
String sql = "select acc_id,acc_name,acc_pass,acc_balance,acc_pic from account";
RowMapper<Account> rowMapper = null;
//会将查询结果集的行映射成实体类的一个对象
rowMapper = new BeanPropertyRowMapper<>(Account.class);
//会根据查询的列名和实体类Javabean风格的属性名自动匹配,列名如果带下划线,则会匹配对应驼峰的属性
List<Account> accounts = jdbcTemplate.query(sql, rowMapper);
System.out.println(accounts);
}
//查询单行单列数据SELECT COUNT(acc_id) FROM account;
@Test
public void testQueryOneData() {
String sql = "SELECT COUNT(acc_id) FROM account where acc_id > ?";
int x = jdbcTemplate.queryForObject(sql, Integer.class, 10);
System.out.println(x);
}
//查询多笔数据没有对应的实体类 SELECT MAX(acc_balance) max_b,MIN(acc_balance) min_b FROM account;
@Test
public void testQueryForMap() {
String sql = "SELECT MAX(acc_balance) max_b,MIN(acc_balance) min_b FROM account";
Map<String,Object> map = jdbcTemplate.queryForMap(sql);
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key + "---------" + map.get(key));
}
}
}
JdbcTemplate在项目中的使用
所有的项目开发中,后台开发都是遵循三层架构:
- 控制层
- 业务层
- 数据访问层【持久化层】
三层架构中,控制层调用业务层,业务层调用持久化层(数据访问层DAO),持久化层使用JDBC或者由JDBC封装而来的工具或者框架来完成数据库的增删改查的实际操作。那么我们的JdbcTemplate也就意味着实在持久化层使用。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/buybook
user=root
password=3306
maxSize=10
initSize=5
<?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:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
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-4.0.xsd">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}"></property>
<property name="jdbcUrl" value="${url}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="maxPoolSize" value="${maxSize}"></property>
<property name="initialPoolSize" value="${initSize}"></property>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.wanbangee"></context:component-scan>
</beans>
package com.wanbangee.entities;
public class Account {
private Integer accId;
private String accName;
private String accPass;
private Double accBalance;
private String accPic;
public String getAccPic() {
return accPic;
}
public void setAccPic(String accPic) {
this.accPic = accPic;
}
public Integer getAccId() {
return accId;
}
public void setAccId(Integer accId) {
this.accId = accId;
}
public String getAccName() {
return accName;
}
public void setAccName(String accName) {
this.accName = accName;
}
public String getAccPass() {
return accPass;
}
public void setAccPass(String accPass) {
this.accPass = accPass;
}
public Double getAccBalance() {
return accBalance;
}
public void setAccBalance(Double accBalance) {
this.accBalance = accBalance;
}
@Override
public String toString() {
return "Account [accId=" + accId + ", accName=" + accName + ", accPass=" + accPass + ", accBalance="+ accBalance + ", accPic=" + accPic + "]";
}
}
package com.wanbangee.dao;
import java.util.List;
import com.wanbangee.entities.Account;
public interface AccountDao {
public List<Account> selectAllAccount();
}
package com.wanbangee.dao.imp;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.wanbangee.dao.AccountDao;
import com.wanbangee.entities.Account;
@Repository
public class AccountDaoImp implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<Account> selectAllAccount() {
List<Account> accounts = null;
try {
String sql = "select acc_id,acc_name,acc_pass,acc_balance,acc_pic from account";
RowMapper<Account> rowMapper = null;//会将查询结果集的行映射成实体类的一个对象
rowMapper = new BeanPropertyRowMapper<>(Account.class);
//会根据查询的列名和实体类Javabean风格的属性名自动匹配,列名如果带下划线,则会匹配对应驼峰的属性
accounts = jdbcTemplate.query(sql, rowMapper);
return accounts;
} catch (Exception e) {
e.printStackTrace();
return null;//出现异常返回null
}
}
}
package com.wanbangee.service;
import java.util.List;
import com.wanbangee.entities.Account;
public interface AccountService {
public List<Account> selectAllAccount();
}
package com.wanbangee.service.imp;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wanbangee.dao.AccountDao;
import com.wanbangee.entities.Account;
import com.wanbangee.service.AccountService;
@Service
public class AccountServiceImp implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public List<Account> selectAllAccount() {
return accountDao.selectAllAccount();
}
}
package com.wanbangee.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.wanbangee.entities.Account;
import com.wanbangee.service.AccountService;
@Controller
public class AccountController {
@Autowired
private AccountService accountService;
public Map<String,Object> selectAllAccount(){
List<Account> accounts = accountService.selectAllAccount();
Map<String,Object> map = new HashMap<>();
if(accounts == null) {
map.put("code", 1003);
map.put("message", "执行失败");
}else {
if(accounts.isEmpty()) {
map.put("code", 1002);
map.put("message", "查无此数");
}else {
map.put("code", 1001);
map.put("message", "执行成功");
map.put("accounts", accounts);
}
}
return map;
}
}
package com.wanbangee.test;
import static org.junit.Assert.*;
import java.util.Map;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.wanbangee.controller.AccountController;
public class TestMVCJUnit {
ConfigurableApplicationContext act = null;//成员变量默认为null的不写也行
AccountController accountController = null;
@Before
public void setUp() throws Exception {
act = new ClassPathXmlApplicationContext("applicationContext.xml");
accountController = act.getBean(AccountController.class);
}
@After
public void tearDown() throws Exception {
act.close();
}
@Test
public void test() {
Map<String,Object> map = accountController.selectAllAccount();
//将所有的键封装进set集合,
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
System.out.println(map.get(key));
}
}
}
使用具名参数NamedParameterJdbcTemplate
在经典的JDBC的操作做那个,SQL参数是使用占位符?表示,并且受到位置的限制,定位参数的问题就存在于,
如果参数过多,或者参数的位置发生变化。这个时候我们就可以对参数进行绑定, 绑定参数指的是给参数起一个别名,
而后通过别名给参数设置值,这种操作形式叫做具名参数,在sql中 用 ‘:别名’的方式 替换‘?’号占位符。
具名参数使用NamedParameterJdbcTemplate进行操作,
且NamedParameterJdbcTemplate是JdbcTemplate的一个子类。
使用NamedParameterJdbcTemplate步骤:
1.编写Spring的配置文件
<!-- 导入外部的属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${url}"></property>
<property name="driverClass" value="${driver}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="maxPoolSize" value="${maxSize}"></property>
<property name="initialPoolSize" value="${initSize}"></property>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 具名参数的jdbcTemplate -->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<!--property属性注入,set注入,调用无参构造器, constructor-arg构造注入,调有参构造器
具名参数类NamedParameterJdbcTemplate中没有无参构造器,只有有参构造器-->
</bean>
Junit 是标准的单元测试工具
private ApplicationContext act = null;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate = null;
@Before
public void setUp() throws Exception {
//每个@Test注解的测试方法执行之前都会执行setUp方法
act = new ClassPathXmlApplicationContext("applicationContext.xml");
namedParameterJdbcTemplate = act.getBean(NamedParameterJdbcTemplate.class);
}
@After
public void tearDown() throws Exception {
//每个@Test注解的测试方法执行之后都会执行tearDown方法
}
//给参数起别名,参数别名不能一样。用Map集合来封装参数,参数以键值对形式传入Map对象,参数名与key要一一对应。
@Test
public void testInsert() {
String sql = "insert into account(acc_name,acc_pass,acc_balance) values(:name,:pass,:balance)"; //sql中使用 :参数别名 替代 ?
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("name", "小马");//Map键值对为参数设置值
paramMap.put("pass", "123");
paramMap.put("balance", 4000);
namedparameterJdbctemplate.update(sql, paramMap);//把参数放入map键值对中,把map对象传入
}
使用具名参数的好处在于,多个参数我们不需要再按照顺序设置,直接使用参数名即可,便于维护,
缺点就是比较麻烦,在实际开发中,参数肯定是通过具名参数设置的。
总结
JdbcTemplate充其量就是一个JDBC的小工具,和ORM框架是没有办法相比的;
JdbcTemplate在使用过程中,比传统原生的JDBC的操作或者是DBUtil更加好用,更加简洁;
JdbcTemplate可以在sql中使用占位符,NamedParameterJdbcTemplate可以使用具名参数;
JdbcTemplate在开发中的使用过程一定要掌握;