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 查询返回某个值

  1. 查询表里有多少条记录,返回是某个值
  2. 使用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 事务操作方式

上一小节的示例中,如果转账过程中突然发生了异常,则可能会出现问题,为了解决这一问题,可以使用事务进行解决
  1. 事务一般添加到JavaEE三层结构的Service层(业务逻辑层)
  2. 在Spring中进行事务管理操作,有两种方式:

    • 编程式事务管理:通过编程对事务进行管理
    • 声明式事务管理:通过配置的方式进行管理,平时使用较多
  3. 声明式事务管理

    1. 注解方式
    2. 基于xml配制文件方式
  4. 在Spring进行声明式事务管理时,底层使用了AOP实现
  5. 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;
    }
}
最后修改:2023 年 11 月 14 日
如果觉得我的文章对你有用,请随意赞赏