1. 概念和准备
1.1 什么是JdbcTemplate
Spring框架对Jdbc进行了封装,使用JdbcTemplate方便实现对数据库操作
1.2 准备工作
引入相关jar包
- druid
- mysql-connector
- spring-jdbc
- spring-orm
- spring-tx
配置德鲁伊数据库连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://user_db"/>
<property name="username" value="root"/>
<property name="password" value="com.hxuanyu.lcc520"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
配置JdbcTemplate对象,注入DataSource
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
创建service类,创建dao类,在dao中注入jdbcTemplate对象
- 开启组件扫描
- Service
@Service
public class BookService {
@Autowired
private BookDao bookDao;
}
- Dao
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
2. 数据库操作(添加)
对应数据库表创建实体类
public class User {
private String userId;
private String username;
private String ustatus;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUstatus() {
return ustatus;
}
public void setUstatus(String ustatus) {
this.ustatus = ustatus;
}
}
编写service和dao
- 在dao中进行数据库添加操作
调用JdbcTemplate对象中的update方法实现添加操作
- 第一个参数:sql语句
- 第二个操作:可变参数,设置sql语句值
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(User user) {
String sql = "insert into t_user values (?,?,?)";
Object[] args = {user.getUserId(), user.getUsername(), user.getUstatus()};
int update = jdbcTemplate.update(sql, args);
System.out.println("添加完毕,受影响行数:" + update);
}
}
3. 修改和删除操作
@Override
public void updateUser(User user) {
String sql = "update t_user set username=?,ustatus=? where user_id=?";
Object[] args = {user.getUsername(), user.getUstatus(), user.getUserId()};
int update = jdbcTemplate.update(sql, args);
System.out.println("修改成功,受影响行数:" + update);
}
@Override
public void deleteUser(String id) {
String sql = "delete from t_user where user_id=?";
int update = jdbcTemplate.update(sql, id);
System.out.println("删除成功,受影响行数:" + update);
}
4. 查询操作
4.1 查询返回某个值
- 查询表里有多少条记录,返回是某个值
使用JdbcTemplate中的
queryObject()
实现查询返回某个值- 第一个参数:sql语句
- 第二个参数:返回类型Class
/**
* 查询记录数量
* @return 记录数量
*/
@Override
public int selectCount() {
String sql = "select count(*) from t_user";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
4.2 查询返回对象
- 场景:查询图书详情
JdbcTemplate使用
queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
进行查询操作- 第一个参数:sql语句
- 第二个参数:
RowMapper
是接口,针对返回不同类型的数据,使用这个接口中的实现类完成封装 - 第三个参数:sql语句值
@Override
public User findUserInfo(String id) {
String sql = "select * from t_user where user_id=?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
4.3 查询返回集合
通过调用JdbcTemplate中的query()
方法查询
- 第一个参数:sql语句
- 第二个参数:
RowMapper
是接口,针对返回不同类型的数据,使用这个接口中的实现类完成封装 - 第三个参数:sql语句值
4.4 批量操作
4.4.1 批量添加
使用JdbcTemplate的banchUpdate(String sql, List<Object[]> batchArgs)
方法,
- 第一个参数为sql语句
- 第二个参数:是一个List集合,添加多条记录
@Override
public void bantchList(List<Object[]> list) {
String sql = "insert into t_user values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, list);
System.out.println(Arrays.toString(ints));
}
List<Object[]> objects = new ArrayList<>();
Object[] o1 = {"1", "hxuanyu", "a"};
Object[] o2 = {"2", "lcc", "b"};
Object[] o3 = {"3", "test", "c"};
objects.add(o1);
objects.add(o2);
objects.add(o3);
userService.bantchAdd(objects);
4.4.2 批量修改
@Override
public void batchUpdate(List<Object[]> list) {
String sql = "update t_user set username=?,ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, list);
System.out.println(Arrays.toString(ints));
}
4.4.3 批量删除
@Override
public void batchDelete(List<Object[]> list) {
String sql = "delete from t_user where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, list);
System.out.println(Arrays.toString(ints));
}
5. 事务操作
5.1 事务的概念
事务是数据库操作的基本单元,逻辑上是一组操作,要么都成功,如果有一个失败则所有操作都失败。典型场景为银行转账,即一个人给另一个人转账的过程,要么一个人转出成功,同时另一个转入成功,要么转账失败,两个账户余额都不变
事务 四大特点(ACID特性):
- 原子性:过程不可分割
- 一致性:操作之前和操作之后总量不变
- 隔离性:多事务操作时,事务之间不会产生相互影响
- 持久性:表中数据在事务提交之后会产生变化
5.2 搭建事务操作环境
- 创建数据库,创建表
- 在项目中创建service和dao接口及实现类,完成对象创建和注入关系
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
<?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"
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">
<context:component-scan base-package="com.hxuanyu"/>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="com.hxuanyu.lcc520"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
在dao中创建两个方法,一个增加余额,一个减少余额
@Override
public void addMoney() {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql, 100, "lucy");
}
@Override
public void reduceMoney() {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql, 100, "mary");
}
5.3 事务操作方式
上一小节的示例中,如果转账过程中突然发生了异常,则可能会出现问题,为了解决这一问题,可以使用事务进行解决
- 事务一般添加到JavaEE三层结构的Service层(业务逻辑层)
在Spring中进行事务管理操作,有两种方式:
- 编程式事务管理:通过编程对事务进行管理
- 声明式事务管理:通过配置的方式进行管理,平时使用较多
声明式事务管理
- 注解方式
- 基于xml配制文件方式
- 在Spring进行声明式事务管理时,底层使用了AOP实现
- Spring事务管理提供一个接口:
PlatformTransactionManager
,代表事务管理器,这个接口针对不同框架提供不同实现类
5.4 注解方式实现事务操作
在spring配置文件中配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
在spring配置文件中开启事务注解,首先引入名称空间tx
,再开启事务注解
<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.xsdhttp://www.springframework.org/schema/aop
http://www.springframework.org/schema/tx/spring-tx.xsd"
>
<context:component-scan base-package="com.hxuanyu"/>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="com.hxuanyu.lcc520"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
在service层添加事务注解@Transactional
,该注解可以添加在类或方法上,添加在类上表示类中所有方法都添加了事务,添加在方法上则只为这个方法添加事务
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
public void accountMoney() {
userDao.addMoney();
int a = 3 / 0;
userDao.reduceMoney();
}
}
此外,在注解中可以配置相关的参数
propagation
:事务传播行为- 即多事务方法(对数据产生变化)直接进行调用,这个过程中事务是如何管理的
共有七种传播行为
REQUIRED
:如果有事务在运行,当前方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行(默认)REQUIRED_NEW
:当前的方法必须启动新事物,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起SUPPORTS
:如果有事务在运行,当前的方法就在这个事务内运行,否咋它可以不运行在事务中NOT_SUPPORTES
:当前的方法不应运行在事务中,如果有运行的事务,将它挂起MANDATORY
:当前的方法必须运行在事务中,如果没有正在运行的事务,就抛出异常NEVER
:当前的方法不应该运行在事务中,如果有正在运行的事务,就抛出异常NESTED
:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新事务,并在它自己的事务内运行
ioslation
:事务隔离级别为了解决事务的隔离性而设计,即多事务操作之间不会产生影响。如果不考虑隔离性,会产生一系列问题:
- 脏读:在多个事务之间,一个未提交的事务读取到另一个未提交事务的数据(致命问题)
- 不可重复读:一个未提交事务读取到另一提交事务修改的数据(现象)
- 幻读:一个未提交事务读取到另一个提交事务添加的数据
通过设置事务隔离级别可以解决:
READ UNCOMMITTED
:三种读问题都无法解决READ COMMITTED
:可解决脏读REPEAYEDABLE READ
:可解决脏读、不可重复读SERIALIZABLE
:可解决脏读、不可重复读、幻读
timeout
:超时时间,事务需要在一定时间内进行提交,不提交则会进行回滚,默认值是-1
,设置时间以秒为单位计算readOnly
:是否只读,表示事务中的操作只能涉及读数据操作,默认值为false
rollbackFor
:回滚,设置出现哪些异常进行事务回滚noRollBackFor
:不回滚,设置出现哪些异常不进行事务回滚
5.5 注解方式实现事务操作
- 在spring配置文件中进行配置
<!--1. 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
- 配置通知
<!--2. 配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
- 配置切入点和切面
<!--3. 配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.hxuanyu.spring5.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
5.6 完全注解开发
创建一个配置类,使用配置类替代xml文件
@Configuration
@ComponentScan(basePackages = "com.hxuanyu")
@EnableTransactionManagement
public class TxConfig {
// 创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource source = new DruidDataSource();
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///user_db");
source.setUsername("root");
source.setPassword("com.hxuanyu.lcc520");
return source;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//在IOC容器中根据类型找到datasource
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}