【Spring】JDBC、AOP、事务

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【Spring】JDBC、AOP、事务

1. Spring中JDBC简介

个人感觉Spring中的JDBC简化了我们之前手写JDBC的步骤,实现了代码的简洁与条理性

如下所示:

public int insert() {
    String sql = "insert into test(id, name)values(?,?)";
    return jdbcTemplate.update(sql, "1", "2");
  }
  public int update() {
    String sql = "update test set name = ? where id = ?";
    return jdbcTemplate.update(sql, "3", "1");
  }
  public int delete(String id) {
    String sql = "delete from test where id = ?";
    return jdbcTemplate.update(sql, id);
  }
  public List<TestModel> selectList() {
    String sql = "select id, name from test where id=?";
    return jdbcTemplate.query(sql, new Object[] { 15 }, new ModelMapper());
  }

基本直接一条语句就可以进行配置,完成数据库的增删改查

因此,我们来看一下具体的配置方法及应用

2. Spring的配置

2.1 pom.xml

首先就是总体xml文件的配置了,主要引进依赖及各种包

== 需要注意的是:我们spring-webmvcspring-jdbc的版本必须保持一致==

z<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>situ</groupId>
  <artifactId>test3</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>test3 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <!-- 依赖 -->
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.20</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.json/json -->
    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
      <version>20160810</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.1</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>test3</finalName>
  </build>
</project>

2.2 数据库连接池

我们在配置完总体xml之后,需要进行数据源的注入

在Spring中有3中注入的方法:JDBC、C3P0、DBCP

2.2.1 JDBC连接池

对于JDBC来说,他属于Spring的内置连接池

配置条件如下:

<!-- 引入属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 配置Spring内置的连接池 -->
       <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!-- 引入属性文件的值 -->            
         <property name="driverClassName" value="${driverClass}"></property>
          <property name="url" value="${jdbcUrl}"></property>
           <property name="username" value="${user}"></property>
           <property name="password" value="${password}"></property>
       </bean>
       <!-- 配置spring的JDBC的模版 -->
       <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
           <property name="dataSource" ref="dataSource"></property>
       </bean>

2.2.2 C3P0连接池

<!-- 引入属性文件 -->
    <context:property-placeholder location="classpath:JDBC.properties"/>
    <!-- 配置C3P0连接池 -->
       <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 引入属性文件的值 -->            
         <property name="driverClass" value="${driverClass}"></property>
          <property name="jdbcUrl" value="${jdbcUrl}"></property>
           <property name="user" value="${user}"></property>
           <property name="password" value="${password}"></property>
    </bean>
       <!-- 配置spring的JDBC的模版 -->
       <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
           <property name="dataSource" ref="dataSource"></property>
       </bean>

2.2.3 DBCP连接池

<!-- 引入属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 配置DBCP连接池 -->
       <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <!-- 引入属性文件的值 -->            
         <property name="driverClassName" value="${driverClass}"></property>
          <property name="url" value="${jdbcUrl}"></property>
           <property name="username" value="${user}"></property>
           <property name="password" value="${password}"></property>
       </bean>
       <!-- 配置spring的JDBC的模版 -->
       <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
           <property name="dataSource" ref="dataSource"></property>
       </bean>

2.2.4 jdbc.properties文件

user=root
password=123456
minPoolSize=5
maxPoolSize=20
initialPoolSize=5
driverClass=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://127.0.0.1:3308/work?useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Hongkong&allowPublicKeyRetrieval=true

2.2.4 总配置文件

<?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.xsd"
  default-autowire="byName">
  <context:annotation-config />
  <context:component-scan base-package="service" />
  <context:property-placeholder
    location="classpath:JDBC.properties" />
  <!-- 数据源 -->
  <!-- <bean id="dataSource" -->
  <!-- class="org.springframework.jdbc.datasource.DriverManagerDataSource"> -->
  <!-- <property name="driverCLassName" value="${driverClass}" /> -->
  <!-- <property name="url" value="${jdbcUrl}" /> -->
  <!-- <property name="username" value="${user}" /> -->
  <!-- <property name="password" value="${pass}" /> -->
  <!-- </bean> -->
  <bean id="dataSource"
    class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driverClass}" />
    <property name="jdbcUrl" value="${jdbcUrl}" />
    <property name="user" value="${user}" />
    <property name="password" value="${password}" />
    <property name="minPoolSize" value="${minPoolSize}"></property>
    <property name="maxPoolSize" value="${maxPoolSize}"></property>
    <property name="initialPoolSize" value="${initialPoolSize}"></property>
  </bean>
  <bean id="springJdbc1" class="service.SpringJdbc1"></bean>
  <!--生成JdbcTemplate -->
  <!--直接使用XML进行注入 -->
  <bean id="jdbcTemplate2"
    class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="SpringDao" class="service.SpringDao"></bean>
  <bean id="Dao1" class="service.Dao1"></bean>
</beans>

3. Java端

3.1 TestModel

最底层的Model层,用来实现类的封装

package service;
public class TestModel {
  private String id;
  private String name;
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  @Override
  public String toString() {
    return "TestModel [id=" + id + ", name=" + name + "]";
  }
}

3.2 SpringDao

这一层主要是利用JdbcTemplate提供的增删改查来实现对数据库的操作

3.2.1 注入的操作

@Autowired
  @Qualifier("jdbcTemplate2")
  private JdbcTemplate jdbcTemplate;

这里使用Autowired + Qualifier(“jdbcTemplate2”),是将我们在context3中进行的XML注入拿过来使用注入private JdbcTemplate jdbcTemplate;这个里面

<bean id="jdbcTemplate2"
    class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>

3.2.2 增删改的操作

public int insert() {
    String sql = "insert into test(id, name)values(?,?)";
    return jdbcTemplate.update(sql, "1", "2");
  }
  public int update() {
    String sql = "update test set name = ? where id = ?";
    return jdbcTemplate.update(sql, "3", "1");
  }
  public int delete(String id) {
    String sql = "delete from test where id = ?";
    return jdbcTemplate.update(sql, id);
  }

3.3.3 查找的操作

对于查找来说,使用的是jdbcTemplate.query

我们来看一下:

第一个参数sql:数据库语句

第二个参数:一个Object的数组,也就是填充我们的问号

第三个参数:ModelMapper(映射)

public List<TestModel> selectList() {
    String sql = "select id, name from test where id=?";
    return jdbcTemplate.query(sql, new Object[] { 15 }, new ModelMapper());
  }

我们重点来看一下这个映射

对于映射而言,我们首先要知道这是干什么的?

我们知道,查询和增删改最大的操作不同就是它多一步返回ModelList值的操作,也就是ResultSet rs的获取

所以,我们需要获取这个ModelList,来进行返回

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class ModelMapper implements RowMapper<TestModel> {
  // 映射ORM
  // 查询出来的值来进行赋值
  // 查询几条记录 就会映射几次
  public TestModel mapRow(ResultSet rs, int rowNum) throws SQLException {
    TestModel testModel = new TestModel();
    testModel.setId(rs.getString("id"));
    testModel.setName(rs.getString("name"));
    return testModel;
  }
}

因为我们返回的是一个集合,所以,集合里面有几条数据,就映射几次

另一种写法(内部类)

public List<TestModel> selectList2() {
    String sql = "select id, name from test where 1=1";
    return jdbcTemplate.query(sql, new Object[] { 15 }, new ModelMapper() {
      public TestModel mapRow(ResultSet rs, int rowNum) throws SQLException {
        TestModel testModel = new TestModel();
        testModel.setId(rs.getString("id"));
        testModel.setName(rs.getString("name"));
        return testModel;
      }
    });
  }

当然,我们也进行查询后返回别的值,如下:

public void queryForObject() {
    String sql = "select id, name from test where id=?";
    TestModel model = jdbcTemplate.queryForObject(sql, new Object[] { 15 }, new ModelMapper());
    System.out.println(model);
    String sql2 = "select id, name from test where id=?";
    String name = jdbcTemplate.queryForObject(sql2, new Object[] { 1 }, String.class);
    System.out.println(name);
  }

第一种利用queryForObject来返回一个Model进行输出

第二种利用String.class加载String的类,进行反射

3.3 Main(类似Service层,主要进行测试用)

  • 通过ApplicationContext context = new ClassPathXmlApplicationContext("context3.xml");创里连接
  • 获取SpringDao的对象context.getBean("SpringDao")
  • 最后,调用dao.test()进行输出
<bean id="SpringDao" class="service.SpringDao"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("context3.xml");
    SpringDao dao = (SpringDao) context.getBean("SpringDao");
    dao.test();
    // 输出111
  }
}

4. AOP、事务

== AOP是个啥?==

专业术语:面向切面编程

通俗提懂:我们把每一个层分开,在每一个层进行操作,如事务

== 事务是啥呢?==

这里简单说一下,我所理解的事务也就是:

比如一个部门表,一个成员表,当部门表被删除时,我们也要删除相对应的成员信息,也就是执行两条SQL语句

假如这时候我们把部门表给删除了,我们再去删除成员表中的信息时,出现了一点小意外,导致我们成员表信息没办法删除,这时候就尴尬了~

部门表已经没了,可成员对应的部门还存在,这。。。。有点匪夷所思

所以,我们用事务来解决这个问题

我们把所有的要执行的SQL语句放在一个事务中,假如其中有一个报错,则直接进行回滚,我们之前执行的所有的操作语句都还原,这样就可以避免上述尴尬情况的发生。

4.1 事务的配置

对于事务来说,我们也需要进行文件的配置

<?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: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 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd"
  default-autowire="byName">
  <bean id="dao1" class="service.Dao1" />
  <bean id="springDao" class="service.SpringDao" />
  <bean id="service1" class="service.Serivce1" />
  <!-- 事务管理器 -->
  <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
    <!-- 需要管理的数据源 -->
  </bean>
  <tx:advice id="advice"
    transaction-manager="transactionManager">
    <tx:attributes>
      <!-- <tx:method name="*" propagation="REQUIRED" /> -->
      <tx:method name="delete*" propagation="REQUIRED" />
      <tx:method name="update*" propagation="REQUIRED" />
      <tx:method name="insert*" propagation="REQUIRED" />
    </tx:attributes>
  </tx:advice>
  <!-- 用切点把事务切进去 -->
  <aop:config>
    <aop:pointcut expression="execution(* service.*.*(..))"
      id="pointcut" />
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut" />
  </aop:config>
</beans>

第一部分,也就是事务管理器,这个是干啥的呢?

我们在上面从数据池中拿出来的东西,也就是数据源,要在这进行管理

这里我们使用了别的xml文件的内容,我们怎么可以使用的呢?

建立一个spring.xml,将两个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:p="http://www.springframework.org/schema/p"
  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"
  default-autowire="byName">
  <context:annotation-config />
  <context:component-scan base-package="service" />
  <import resource="context3.xml"></import>
  <import resource="test3.xml"></import>
</beans>

第二部分,我们规定的操作,这里注意一下:name="delete*"这个*号的意思,也就以delete开头所有方法

第三部分,利用AOP切片,将事务切入进去,execution(* service.*.*(..)) 意思为:service包下的任意类下的任意方法的任意参数

4.2 配置另外的类

和上面差不多,主要就是修改一下Dao层里关于SQL语句的书写

我们在定义一个Model2,一个Dao2

Model2

package service;
public class TestModel2 {
  private String id;
  private String name;
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  @Override
  public String toString() {
    return "TestModel [id=" + id + ", name=" + name + "]";
  }
}

Dao2

package service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
public class Dao1 {
  @Autowired
  @Qualifier("jdbcTemplate2")
  private JdbcTemplate jdbcTemplate;
  public void test1() {
    System.out.println("111");
  }
  public int insert() {
    String sql = "insert into test1(id, name)values(?,?)";
    return jdbcTemplate.update(sql, "1", "2");
  }
  public int update() {
    String sql = "update test1 set name = ? where id = ?";
    return jdbcTemplate.update(sql, "3", "1");
  }
  public int delete(String id) {
    String sql = "delete from test1 where id = ?";
    return jdbcTemplate.update(sql, id);
  }
  // 三个参数
  // sql : SQL语句
  // Object : 你要赋值的一个数组
  public List<TestModel> selectList() {
    String sql = "select id, name from test1 where id=?";
    return jdbcTemplate.query(sql, new Object[] { 15 }, new ModelMapper());
  }
  // 内部类的写法
  public List<TestModel> selectList2() {
    String sql = "select id, name from test1 where 1=1";
    return jdbcTemplate.query(sql, new Object[] { 15 }, new ModelMapper() {
      public TestModel mapRow(ResultSet rs, int rowNum) throws SQLException {
        TestModel testModel = new TestModel();
        testModel.setId(rs.getString("id"));
        testModel.setName(rs.getString("name"));
        return testModel;
      }
    });
  }
  public void queryForObject() {
    String sql = "select id, name from test1 where id=?";
    TestModel model = jdbcTemplate.queryForObject(sql, new Object[] { 15 }, new ModelMapper());
    System.out.println(model);
    String sql2 = "select id, name from test1 where id=?";
    String name = jdbcTemplate.queryForObject(sql2, new Object[] { 1 }, String.class);
    System.out.println(name);
  }
}

4.3 事务的实现

到了最后一步,也就是真正实现我们的事务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Serivce1 {
  @Autowired
  private SpringDao springDao;
  @Autowired
  private Dao1 dao1;
  public void delete(String id) {
    dao1.delete(id);
    springDao.delete(id);
  }
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    Serivce1 serivce1 = (Serivce1) context.getBean("service1");
    serivce1.delete("1");
  }
}

对于下面这两段代码,主要就是注入Dao1和SpringDao

@Autowired
  private SpringDao springDao;
  @Autowired
  private Dao1 dao1;

下面代码

  • 调用spring.xml文件
  • 获取service1的实例化
public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    Serivce1 serivce1 = (Serivce1) context.getBean("service1");
    serivce1.delete("1");
  }

最后事务成功实现

4.4 事务的验证

public int delete(String id) {
    if (id.equals("1")) {
      int n = 1 / 0;
    }
    String sql = "delete from test1 where id = ?";
    return jdbcTemplate.update(sql, id);
  }

将Dao的删除改为以上代码,即可验证其合理性


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7天前
|
Java Maven 数据安全/隐私保护
详解 Java AOP:面向方面编程的核心概念与 Spring 实现
详解 Java AOP:面向方面编程的核心概念与 Spring 实现
19 1
|
5天前
|
XML 安全 Java
Spring高手之路19——Spring AOP注解指南
在本文中,我们将深入探索Spring AOP(面向切面编程)的核心概念及其在现代Spring应用中的实际应用。从基本的注解使用到复杂的切面配置,本文将一步步指导你如何利用Spring AOP提升代码的模块化,帮助你在Spring开发路上更进一步。
19 3
Spring高手之路19——Spring AOP注解指南
|
2天前
|
XML Java API
Spring AOP切点和通知机制的深度解析
Spring AOP切点和通知机制的深度解析
13 4
|
1天前
|
缓存 监控 安全
在 Spring Boot 中使用 AOP(Aspect-Oriented Programming)实现日志记录功能
在 Spring Boot 中使用 AOP(Aspect-Oriented Programming)实现日志记录功能
13 1
|
3天前
|
XML Java 数据库
Spring框架第五章(声明式事务)
Spring框架第五章(声明式事务)
|
4天前
|
设计模式 网络安全 开发工具
|
7天前
|
缓存 Java uml
Spring压轴题:当循环依赖遇上Spring AOP
Spring压轴题:当循环依赖遇上Spring AOP
14 1
|
3天前
|
设计模式 SQL Java
Spring框架第四章(AOP概念及相关术语)
Spring框架第四章(AOP概念及相关术语)
|
1月前
|
安全 Java Spring
Spring之Aop的底层原理
Spring之Aop的底层原理
|
1月前
|
设计模式 Java uml
Spring AOP 原理
Spring AOP 原理
16 0