04-JdbcTemplate

nobility 发布于 2022-03-23 161 次阅读


JdbcTemplate

在引入IOC和AOP模块的基础上在引入Data Access/Integration模块,同时也要讲数据库驱动引入

一般使用Maven引入spring-jdbc就会数据访问模块引入

基本使用

将jdbcTemplate对象交由SpringIOC容器管理,具体配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  <!--使用Spring中提供的数据库连接池-->
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>  <!--数据库驱动-->
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>  <!--数据库连接URL-->
    <property name="username" value="root"/>  <!--数据库用户名-->
    <property name="password" value="root"/>  <!--数据库密码-->
  </bean>
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>  <!--注入连接池-->
  </bean>
</beans>

下面测试数据库连接

public static void main(String[] args) {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  JdbcTemplate jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
  jdbcTemplate.execute("create table test(id int,name varchar(20))");  //execute用于表级操作,无返回值,不常用
}

更新操作

方法名 描述
int update(String sql,Object ...args) 执行插入、删除和更新的SQL,args为SQL中的占位符内容,返回影响行数
int[] batchUpdate(String[] sql,List<Object[]> args) 执行多条更新SQL,args为SQL中的占位符内容,与多条SQL顺序一一对应,返回每条SQL影响的行数

查询操作

查询单个简单数据项

方法名 描述
T queryForObject(String sql,Class<T> clazz,Object ...args) 执行SQL,返回单个字段值对象,类型由clazz参数指定,args为SQL中的占位符内容
List<T> queryForList(String sql,Class<T> clazz,Object ...args) 执行SQL,返回一列单个字段值对象,类型由clazz参数指定,args为SQL中的占位符内容

查询复杂对象(Map)

方法名 描述
Map<String,Object> queryForMap(String sql,Object ...args) 执行查询SQL,并将结果处理成Map集合,args为SQL中的占位符内容
List<Map<String,Object>> queryForList(String sql,Object ...args) 执行查询SQL,并将结果处理成Map的List集合,args为SQL中的占位符内容

查询复杂对象(实体)

方法名 描述
T queryForObject(String sql,RowMapper<T> rm,Object ...args) 执行查询SQL,并将结果处理成JavaBean,一般使用BeanPropertyRowMapper(Class clazz)对象,该类构造接收一个JavaBean类对象
List<T> query(String sql,RowMapper<T> rm,Object ...args) 执行查询SQL,并将结果处理成JavaBean的List集合,一般使用BeanPropertyRowMapper(Class clazz)对象,该类构造接收一个JavaBean类对象

手动实现RowMapper接口,对于连接查询可能会用到

jdbcTemplate.queryForObject("select * form students where id = 1", new RowMapper<Student>() {
  @Override
  public Student mapRow(ResultSet resultSet, int i) throws SQLException {
    //resultSet是JDBC的结果集,i是处理的当前行号
    //一般使用就向下面这样,从结果集中取出数据赋值给对象,在返回即可
    Student student = new Student();
    student.setStuId(resultSet.getInt("stu_id"));
    student.setStuName(resultSet.getString("stu_name"));
    return student;
  }
});

事务

Spring中的事务接口架构

Spring将事务抽象成3个接口,分别是事务属性定义接口、事务管理器接口,事务状态接口,其中事务管理其接口的实现有多种,大致流程就是定义好事务属性,事务管理器执行事务改变事务状态

事务属性
  • 回滚规则:默认情况下,事务遇到运行期异常(RuntimeException及其子类)自动回滚,而在遇到检查型异常(IOException、SQLException、FileNotFoundException等)时不回滚
  • 事务超时时间:事务在特定时间内为完成,则自动回滚,默认是-1,不会超时
  • 只读事务:利用数据库事务只读属性进行特定的优化处理,对于不同的数据库行为可能不同,Oracle的更新语句将不起作用,而MySQL的更新语句将会抛出异常
  • 事务隔离级别:定义是否可接收脏读、不可重复读、幻读
    • ISOLATION_DEFAULT:使用数据库默认的隔离级别
    • ISOLATION_READ_UNCOMMITTED:读未提交
    • ISOLATION_READ_COMMITTED:读已提交
    • ISOLATION_REPEATABLE_READ:可重复读
    • ISOLATION_SERIALIZABLE:串行化读
  • 事务传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
    • PROPAGATION_REQUIRED必须,当前方法必须允许在事务中,若当前事务存在,则在该事务中运行,否则会创建一个新事务
    • PROPAGATION_SUPPORTS忍受,当前方法无需事务上下文,但若当前事务存在,就在改事务中运行,不存在也无所谓
    • PROPAGATION_MANDATORY强制,当前方法必须在事务中运行,若当前事务不存在会抛出异常
    • PROPAGATION_REQUIRED_NEW:当前方法必须允许在自己的事务中,若当前事务存在,则在该方法执行期间,当前事务会被挂起
    • PROPAGATION_NOT_SUPPORTS:当前方法不应该运行在事务中,若存在当前事务,则在该方法执行期间,当前事务会被挂起
    • PROPAGATION_NEVER:当前方法不应该运行在事务上下文中,若当前事务存在,会抛出异常
    • PROPAGATION_NESTED:若当前事务存在,该方法将在嵌套事务中运行,若当前事务不存在,那么会创建一个新事务
事务管理器

根据给定的规则(事务属性)进行事务的提交或回滚

  • JDBC事务管理器:使用连接对象控制,局限与一个数据库连接内,但使用简单
  • JTA事务管理器:与实现无关,与协议无关的API,功能强大,可跨越多个数据库或DAO,使用比较复杂
  • 等等
事务状态

用于描述当前事务的运行状态,通过事务管理器获取事务状态实例

事务管理器方式

public static void main(String[] args) throws PropertyVetoException {
  /*1.创建数据源*/
  DriverManagerDataSource dataSource = new DriverManagerDataSource();
  dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
  dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
  dataSource.setUsername("root");
  dataSource.setPassword("root");

  /*2.创建JDBC事务管理器*/
  DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);

  /*3.创建事务管理器属性对象,根据需要,设置事务管理器的相关属性*/
  DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition(); // 定义事务属性
  defaultTransactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);  // 设置传播行为属性

  /*4.获得事务状态对象*/
  TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition);

  /*5.根据事务管理器获取的数据源,创建JdbcTemplate对象*/
  JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceTransactionManager.getDataSource());

  try {
    jdbcTemplate.update("update students set stu_sex = 1 where stu_id = 1");
    dataSourceTransactionManager.commit(transactionStatus); //提交事务
  } catch (Exception e) {
    dataSourceTransactionManager.rollback(transactionStatus);  //回滚事务
    e.printStackTrace();
  }
}

模板事务方式

无返回值的事务
public static void main(String[] args) throws PropertyVetoException {
  /*1.创建数据源*/
  DriverManagerDataSource dataSource = new DriverManagerDataSource();
  dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
  dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
  dataSource.setUsername("root");
  dataSource.setPassword("root");

  /*2.创建JDBC事务管理器*/
  DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);

  /*3.更根据事务管理器,创建事务模板对象,根据需要,设置事务的相关属性*/
  TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);
  transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);  //设置事务隔离级别

  /*4.使用事务模板执行事务*/
  transactionTemplate.execute(new TransactionCallbackWithoutResult() {  //无返回值的事务使用TransactionCallbackWithoutResult接口实现其内部类
    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {  //transactionStatus是事务状态对象,用于事务回滚
      try {
        /*使用JdbcTemplate数据库操作*/
        // JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        // jdbcTemplate.execute("insert into students(stu_name,stu_sex) value('张三',0)");

        /*使用简单模板化新增数据,不常用了解一下*/
        SimpleJdbcInsert simpleInsert = new SimpleJdbcInsert(dataSource);
        simpleInsert.withTableName("students").usingColumns("stu_name", "stu_sex");
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("stu_name", "张三");
        parameters.put("stu_sex", 1);
        simpleInsert.execute(parameters);
        //没有遇到异常,事务自动提交
      } catch (Exception e) {
        transactionStatus.setRollbackOnly();  //回滚事务
        e.printStackTrace();
      }
    }
  });
}
有返回值的事务
public static void main(String[] args) throws PropertyVetoException {
  /*1.创建数据源*/
  DriverManagerDataSource dataSource = new DriverManagerDataSource();
  dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
  dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
  dataSource.setUsername("root");
  dataSource.setPassword("root");

  /*2.创建JDBC事务管理器*/
  PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);

  /*3.更根据事务管理器,创建事务模板对象,根据需要,设置事务的相关属性*/
  TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
  transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);  //设置事务隔离级别

  /*4.使用事务模板执行事务*/
  Map<String, Object> map = transactionTemplate.execute(new TransactionCallback<Map<String, Object>>() {  //有返回值的事务使用TransactionCallback接口实现其内部类,该泛型就是返回值类型
    public Map<String, Object> doInTransaction(TransactionStatus transactionStatus) {   //transactionStatus是事务状态对象,用于事务回滚
      Map<String, Object> map = new HashMap<>();
      JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
      try {
        map = jdbcTemplate.queryForMap("select * from students where stu_id = 1");
        //没有遇到异常,事务自动提交
      } catch (Exception e) {
        transactionStatus.setRollbackOnly();  //回滚事务
        e.printStackTrace();
      }
      return map;
    }
  });
  System.out.println(map);  //输出事务返回值
}

声明时事务方式

基于AOP机制,对方法前后进行拦截,通过配置就可以完成对事务的管理,自动提交或回滚事务

xml方式(tx拦截器)
<?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: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-4.3.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    http://www.springframework.org/schema/tx     
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
  <!--
  首先在aop约束文件的基础上,添加 xmlns:tx="http://www.springframework.org/schema/tx" 约束文件
  再在 xsi:schemaLocation属性后增加 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
  之后使用下面标签才能有代码提示
  -->
  <!--使用Spring中提供的数据库连接池-->
  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
  </bean>

  <!--jdbcTemplate注入连接池-->
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <!--创建jdbc事务管理器,注入连接池-->
  <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <!--要自动执行事务提交和回滚的类-->
  <bean id="userDaoImpl" class="com.UserDaoImpl"/>

  <!--根据jdbc事务管理器,创建事务通知-->
  <tx:advice id="dataSourceTransactionManagerAdvice" transaction-manager="dataSourceTransactionManager">
    <tx:attributes>  <!--配置事务属性-->
      <tx:method name="get*" read-only="true"/>  <!--以get开头的方法,设置为只读事务-->
      <tx:method name="*"/>  <!--其他方法使用默认的事务属性即可-->
    </tx:attributes>
  </tx:advice>

  <!--使用aop对事务方法进行代理-->
  <aop:config>
    <aop:advisor advice-ref="dataSourceTransactionManagerAdvice" pointcut="execution(* com.UserDaoImpl.*())"/>
  </aop:config>
</beans>

下面是测试代码

package com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class UserDaoImpl implements UserDao {
  @Override
  public void transaction() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    JdbcTemplate jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
    jdbcTemplate.update("update students set stu_sex = 1 where stu_id = 1");
  }
  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) applicationContext.getBean("userDaoImpl");  //必须从IOC容器中获取
    userDao.transaction();  //执行事务方法
  }
}
注解方式
<?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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  <!--使用Spring中提供的数据库连接池-->
  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
  </bean>

  <!--jdbcTemplate注入连接池-->
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <!--jdbc事务管理器注入连接池-->
  <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <!--要自动执行事务提交和回滚的类-->
  <bean id="userDaoImpl" class="com.UserDaoImpl"/>

  <!--启动使用注解实现声明式事务管理的支持-->
  <tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
</beans>

下面是测试代码

package com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

@Transactional()  //使用该注解进行声明事务,该注解中也可配置事务的属性
//该注解可作用于类和公共方法上,作用于类上时表示该类下的所有公共方法增加事务功能
public class UserDaoImpl implements UserDao {
  @Override
  public void transaction() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    JdbcTemplate jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
    jdbcTemplate.update("update students set stu_sex = 1 where stu_id = 1");
  }
  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) applicationContext.getBean("userDaoImpl");  //必须从IOC容器中获取
    userDao.transaction();  //执行事务方法
  }
}
此作者没有提供个人介绍
最后更新于 2022-03-23