SpringBoot整合数据库连接

news2024/11/30 11:42:48

JDBC

 1、数据库驱动

JDBC(Java DataBase Connectivity),即Java数据库连接。简而言之,就是通过Java语言来操作数据库。

JDBC是sun公司提供一套用于数据库操作的接口.

java程序员只需要面向这套接口编程即可。不同的数据库厂商(MySQLOracleDB2、SQLServer 等),需要用实现类去实现这套接口,再把这些实现类打包(数据驱动jar包),并提供数据驱动jar包给我们使用。   

驱动:就是一个jar包,里面包含了JDBC的实现类

想要通过JDBC连接并操作Mysql数据库,我们需要下载一个Mysql数据库驱动jar包。所以我们下面都能看到引入了这个依赖

<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2、JDBC编程六步

第一步:注册驱动    作用:告诉Java程序,即将要连接的是哪个数据库

通过DriverManager.registerDriver(driver)注册驱动

String className = "com.mysql.cj.jdbc.Driver";
//1. 注册驱动
Class clazz = Class.forName(className);
Driver driver = (Driver) clazz.newInstance();
DriverManager.registerDriver(driver);

第二步:获取连接    表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信使用完之后一定要关闭通道

  • 通过DriverManager.getConnection(url,user,pwd)获取连接
  • Connection连接对象不能随意创建,最后使用完要手动关闭
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/spring-boot-demo?serverTimezone=UTC";
String user = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url, user, password);

第三步:获取数据库操作对象    专门执行sql语句的对象

  • 一个数据库连接对象可以创建多个数据库操作对象
  • 通过conn.createStatement()获取数据库操作对象

第四步:执行SQL语句    DQL DML…

  • 通过数据库操作对象statement.executeUpdate(sql)编译执行SQL
  • JDBC编写SQL语句不需要以分号结尾
  • 数据库管理系统会将编写好的SQL语句编译并执行

第五步:处理查询结果集    只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集
第六步:释放资源    使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭

  • 一个Connection可以创建多个Statement一个Statement可以得出多个ResultSet,所以先关闭ResultSet,再关闭Statement,最后关闭Connection

3、PreparedStatement

SQL注入实例

定义SQL语句框架的时候,使用PreparedStatement数据库操作对象,这个是预编译对象,先将SQL语句框架进行了编译,然后给参数?动态赋值

Statement 和 PreparedStatement 对比
PreparedStatement可以防止 SQL 注入,执行效率高
SQL语句对于Statement来说是:编译一次执行一次
SQL语句对于PreparedStatement来说,是编译一次执行N次
原因:数据库管理系统(DBMS)厂商实现了JDBC接口,DBMS将编译后的SQL语句保存在DBMS中,由于DBMS中有很多编译好的SQL语句,这时通过同一个PreparedStatement对象进行赋值,便会找到其对应的PreparedStatement对象,从而实现其参数的赋值,即:一次编译多次执行。
PreparedStatement是类型安全的,编译期可以检查传入参数类型


4、JDBC事务

  • JDBC默认情况下,事务是自动提交的:即在JDBC中执行一条DML语句就执行了一次事务
  • 将事务的自动提交,修改为手动提交即可避免自动提交
  • 在事务执行的过程中,任何一步出现异常都要进行回滚
  • 设置手动提交事务:conn.setAutoCommit(false);
  • 事务提交:conn.commit();
  • 事务回滚:conn.rollback();

// 开启事务 conn.setAutoCommit(false);

// 提交事务(事务结束) conn.commit();


SpringBoot整合JdbcTemplate

1、启动器依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2、yml配置

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/spring-boot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    initialization-mode: always
    continue-on-error: true
    schema:
    - "classpath:db/schema.sql"
    data:
    - "classpath:db/data.sql"
    hikari:
      minimum-idle: 5
      connection-test-query: SELECT 1 FROM DUAL
      maximum-pool-size: 20
      auto-commit: true
      idle-timeout: 30000
      pool-name: SpringBootDemoHikariCP
      max-lifetime: 60000
      connection-timeout: 30000

3、JdbcTemplate的使用

1.增删改:

String sql = "insert into t_user values(null,?,?)";
int result = jdbcTemplate.update(sql, "李四", 999);

2.查一条数据为一个实体类对象

让我惊讶的是,居然可以把下划线自动转驼峰

String sql = "select * from t_user where user_id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);

3.查询多条数据为一个list集合

RowMapper设置映射关系,new BeanPropertyRowMapper<>(User.class)

String sql = "select * from t_user";
List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
list.forEach(user -> System.out.println(user));

4.查询单行单列的值

 把Integer.class换成 int.class也可以

String sql = "select count(*) from t_user";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);

JdbcTemplate封装了许多SQL操作,具体可查阅官方文档JdbcTemplate (Spring Framework 6.0.12 API)


SpringBoot整合mybatis

1、依赖

mybatis-spring-boot-starter:

<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

注意:有2种方式可以指定重新指定mysql的版本


2、yml配置

server:
  port: 8089  #tomcat端口号
logging:
  level:
     com.atguigu.dao: debug # 配置日志
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/springboot?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  type-aliases-package: com.atguigu.pojo

指定mybati sql映射文件位置

如数据库里这个字段是这样的user_id,实体类里是这样的userId。mybatis默认不开启驼峰命名规则。即默认情况下无法匹配。

mybatis默认不开启驼峰命名规则。即默认情况下无法匹配。

不用去指定全局配置文件mybatis-config.xml; 而使用mybatis.configuration去代替它。


3、@Mapper注解

mybatis接口要创建代理对象,原来是通过sqlSession.getMapper(UserMapper.class),现在加上@Mapper才行

告诉mybatis这是一个Mapper接口,来操作数据库

@Mapper
public interface AccountMapper{
 
  public Account getAcct(Long id);
 
}

当然,每一个mapper接口都加的话太麻烦了,直接在启动类里做处理就好了

 @MapperScan(basePackage=“”),扫描包,提供代理对象

@SpringBootApplication
@MapperScan(basePackages = "com.atguigu.dao")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

 在测试类 MybatisTest【一定要被启动类覆盖】

@RunWith(SpringJUnit4ClassRunner.class) //指定Junit核心运行类
@SpringBootTest //自动提供IOC容器
public class MybatisTest {
    @Autowired
    private UserMapper userMapper ;

    @Test
    public void findAll() {
        List<User> users = userMapper.selectAll();
        System.out.println(users);
    }
}

测试结果:因为没有重写toString


4、整合mybatis-plus

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.1.0</version>
</dependency>

配置文件

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/spring-boot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    initialization-mode: always
    continue-on-error: true
    schema:
    - "classpath:db/schema.sql"
    data:
    - "classpath:db/data.sql"
    hikari:
      minimum-idle: 5
      connection-test-query: SELECT 1 FROM DUAL
      maximum-pool-size: 20
      auto-commit: true
      idle-timeout: 30000
      pool-name: SpringBootDemoHikariCP
      max-lifetime: 60000
      connection-timeout: 30000
logging:
  level:
    com.xkcoding: debug
    com.xkcoding.orm.mybatis.plus.mapper: trace
mybatis-plus:
  mapper-locations: classpath:mappers/*.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.xkcoding.orm.mybatis.plus.entity
  global-config:
    # 数据库相关配置
    db-config:
      #主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: auto
      #字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
      field-strategy: not_empty
      #驼峰下划线转换
      table-underline: true
      #是否开启大写命名,默认不开启
      #capital-mode: true
      #逻辑删除配置
      #logic-delete-value: 1
      #logic-not-delete-value: 0
      db-type: mysql
    #刷新mapper 调试神器
    refresh: true
  # 原生配置
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true

SpringBoot整合 JPA

1、依赖

spring  data JPA 对  hibernate做了封装,底是Hibernate

添加Spring Data JPA的起步依赖

        <!-- springBoot JPA的起步依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
 
        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

2、yml配置

在application.yml中配置数据库和jpa的相关属性

日志级别:
fatal error warn info debug   级别越低,信息越多

logging:
  level:
    com.atguigu.dao: debug # 配置日志
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/springboot?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    database: mysql  #数据库类型
    show-sql: true  #显示sql
    generate-ddl: true #生成表结构
    hibernate:
      ddl-auto: update  #自动更新表结构
      naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy  #表名字和字段名字的命名策略
server:
  port: 8089  #tomcat端口号

@Id   标明主键
@GeneratedValue  设置主键自增

id属性上没写@Column

很重要的一个类,会根据这个类在数据库中生成表

@Entity
@Table(name = "user")
public class User{
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username")
    private String username;
    @Column(name = "password")
    private String password;
    @Column(name = "name")
    private String name;
 
    //此处省略setter和getter方法... ...
}

UserDao 

public interface UserDao extends JpaRepository<User,Long> {
}

 spring data JPA添加和更新都是save方法、怎么区分:user有没有id

之后编写Controller和service层,这俩层代码跟以前一样,没啥水平。特别强调的是,咱没写mapper映射文件也没写UserDao的实现类,鬼知道怎么就好使了呢

项目跑起来,让自动创建表

idea控制台打印输出 

我们给表里添加俩条数据,然后在postman中发送请求  http://localhost:8089/user/findAll

响应数据如下:

1


SpringBoot事务管理器

1、事务管理器

加入事务管理器,在启动类上加上@EnableTransactionManagement

就可以使用@Transactional事务管理了

TransactionManager是一个空接口

事务管理器 是声明式事务的基础。不管是注解还是xml,这个都得配

在applicationContext.xml中

transaction-manager属性的默认值是transactionManager
如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性


<!--    配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    开启事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager" />

事务:针对连接对象,连接由数据源dataSource提供

原生的:connection连接对象来管理事务


2、开启事务的注解驱动

@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Transactional {

}

开启后通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器处理事务

方式一:[废弃,使用springboot]

环绕通知来实现,因为左边这个标志哈

方式二:【推荐】

当然在之后的springboot项目中,就不这么麻烦了,直接在启动类上加

@EnableTransactionManagement


3、模拟场景:买书

Spring声明式事务 本质就是使用了aop 切面编程,对加了@Transactional注解的方法或者service类的对象进行了扩展。方法调用前后进行了事务管理

模拟场景:买书

模拟买书三步骤,需要进行事务管理

  1. 查询图书价格
  2. 更新图书库存
  3. 更新用户余额

没有事务管理的情况

BookServiceImpl中的买书方法,注意此时buyBook()上没有加@Transactional。

   @Override 
   public void buyBook(Integer bookId, Integer userId) {
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }

测试如下代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class BookControllerTest {
    @Autowired
    private BookController bookController;

    @Test
    public void test1(){
        bookController.buyBook(1,1);
    }
}

报错是情理之中,

因为:从下面2表数据(测试方法执行前)可看得出,admin这个用户余额只有80,它买一本价格为100的《斗破苍穹》就会报错,因为表结构中余额是无符号整数。

那么由于我们没有事务管理,此时表中的数据就不对劲了

t_user中数据不会变化,因为执行这条sql的时候报错了嘛,但是t_book这个表里面的库存减一了,也就是“买书三步骤”中的第三步报错了,但是第二步依旧没回滚。


加入事务管理解决

以 @Transactional 注解为植入点的切点,这样才能知道@Transactional注解标注的方法需要被代理。

如何避免上面那种错呢,很简单就是加一个注解@Transactional,搞定!

    @Override
    @Transactional
    public void buyBook(Integer bookId, Integer userId) {
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }

 我们把t_book中的库存,改回为100,再执行一遍测试代码

bookController.buyBook(1,1);

报错肯定是要报错的,

 但是这时候,t_book表中的库存并没有改,说明事务回滚了!nice

在实际开发中好多都是业务逻辑出错导致的回滚:如库存不够和余额不够。

就拿卖书的例子来说,要是我们没有把金额设置为无符号整数,对数据库来说,整数也可以为负,从逻辑来说,金额不能为负,所以解决方案有2种:

1、从数据库方面来解决:无符号整数unsigned
2、从Java代码方面来解决:没有异常给造一个异常

4、@Transactional 事务属性

@Transactional就是用来定位连接点,可以标识在类或方法上。

@Transactional注解:通过该注解所标识的方法或类中所有的方法会被事务管理器处理事务 

​​​​​​​@Transactional原理是aop 对调用的目标方法进行了扩展
1.方法调用前开启事务 
2.方法调用时它对方法进行了try...catch,如果进入到catch中则回滚事务
3.如果try的最后一行代码能执行到则提交事务

事务的属性

事务的属性有 只读、超时时间、回滚策略、隔离级别、传播行为

    @Transactional(
            readOnly = false,
            timeout = 30,
            //noRollbackForClassName = "java.lang.ArithmeticException",
            noRollbackFor = ArithmeticException.class,
            isolation = Isolation.DEFAULT,
            propagation = Propagation.REQUIRES_NEW
    )

但是不要慌,事务的这些属性都有默认值

1、事务的只读

通过@Transactional注解的readOnly属性设置,默认值为false

若当前的事务设置为只读,则会在数据库层面去优化该操作,比如不加锁!

注意:只有事务中只有查询功能时,才可以设置事务的只读,即readOnly = true
若设置为只读的事务中有任何的增删改操作,则抛出异常:
java.sql.SQLException: Connection is read-only. 

2、事务的超时时间

通过@Transactional注解的timeout属性设置,默认值为-1,表示往死里等(单位秒)

因为事务针对连接来操作,如果连接一直处于阻塞,就会无限制等待下去

注意:若当前事务设置了事务的超时时间,但是在指定时间内没有执行完毕则抛出事务超时异常TransactionTimedOutException,并将事务强制回滚

我们可以让线程阻塞! TimeUnit.SECONDS.sleep(5);

    @Override
    @Transactional(
            timeout = 3
    )
    public void buyBook(Integer bookId, Integer userId) {
        try {
            //线程暂停5秒,可读性强
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }

运行结果,不出所料


3、事务的回滚策略

声明式事务 默认只针对运行时异常回滚,编译时异常不回滚。

  • 运行时异常:不受检异常,没有强制要求try-catch,都会回滚。例如:ArrayOutOfIndex,OutofMemory,NullPointException

  • 编译时异常:受检异常,必须处理,要么try-catch要么throws,都不回滚。例如:FileNotFoundException

通过@Transactional注解的rollbackFor、rollbackForClassName、noRollbackFor、noRollbackForClassName设置

rollbackFor和rollbackForClassName指定的异常必须回滚 ,一般不设置
noRollbackFor和noRollbackForClassName指定的异常不用回滚


rollbackFor和noRollbackFor通过异常的class对象设置
rollbackForClassName和noRollbackForClassName通过异常的全类名设置

noRollbackFor举例

用80去买价格为50一本的《斗罗大陆》,是可以正常买成功的;

但是我们程序最后加了一个数学运算异常,那么按道理来说,会回滚事务。

但是加了noRollbackFor之后,再发生数学运算异常的时候,事务就不会回滚了。。

    @Override
    @Transactional(noRollbackFor = ArithmeticException.class)
    public void buyBook(Integer bookId, Integer userId) {
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
        System.out.println(1/0);
    }

结果就是,发生了数学运算异常,但是事务没有回滚,数据库数据依旧发生了改变


4、事务的隔离级别

通过@Transactional注解的isolation设置事务的隔离级别,一般使用数据库默认的隔离级别.

隔离级别越高,数据一致性就越好,但并发性越弱。

isolation = Isolation.DEFAULT;//表示使用数据库默认的隔离级别
isolation = Isolation.READ_UNCOMMITTED;//表示读未提交
isolation = Isolation.READ_COMMITTED;//表示读已提交
isolation = Isolation.REPEATABLE_READ;//表示可重复读
isolation = Isolation.SERIALIZABLE;//表示串行化

5、事务的传播行为

事务的传播:当A事务方法调用了B事务方法,A方法在执行时,就会将其本身的事务传播给B方法
B方法执行的过程中,使用A传播过来的事务,也可以使用其本身即B的事务

更详细的可以参考: 
22-05-13 西安 jdbc(03) 事务的ACID属性、并发问题、隔离级别;事务传播行为、本地事务_£小羽毛的博客-CSDN博客_jdbc事务 并发

场景模拟:结账

创建CheckoutServiceImpl,这里我们有个很重要的事情,就是结账:checkout()

它是一个事务方法,它调用了BookServiceImpl的事务方法buyBook()

场景模拟:结账
@Service
public class CheckoutServiceImpl implements CheckoutService {
    @Autowired
    private BookService bookService;

    @Override
    @Transactional
    public void checkout(int[] bookIds, int userId) {
        for (int bookId : bookIds) {
            bookService.buyBook(bookId, userId);
        }
    }
}

在默认情况下,即不改变事务的默认属性:

测试:修改用户的余额为120,目的为了让用户可以买第一本书,但是在买第二本的时候,会因为余额不够而报出异常。

bookController.checkout(new int[]{1,2},1);

此时,观察数据库结果发现俩张表数据都无变化,当“有一本书不能买,那就一本书都买不了”

默认的事务传播属性就是,propagation = Propagation.REQUIRED

表示使用A方法传播到B中的事务,若B方法执行的过程中,只要有抛出异常,整个A方法都要回滚,这是默认

---------------------------------------

可以通过propagation属性设置事务的传播行为,现在,不用默认属性了。改为

propagation = Propagation.REQUIRES_NEW

表示在B方法执行中不使用A方法传播到B中的事务,而是开启一个新事务,即使用B本身的事务。
若B方法执行的过程中,只要有抛出异常,B事务方法回滚,A不会回滚,即不影响A中的其他事务的执行(如第二次买书失败不影响第一次买书成成功)

修改代码,重新测试

继续用上面的测试代码测试,提醒一下,现在用户余额是120,他买的起第一本书,买不起第二本

bookController.checkout(new int[]{1,2},1);

测试后,数据库数据发生了改变,也就是从以前的"有一本书不能买,那就一本书都买不了",变成了现在的“能买几本买几本”。


5、事务失效

  • service没有托管给spring
 spring事务生效的前提是,service必须是一个bean对象
  • 方法不是public的
@Transactional只能用于public的方法上,否则会失效
  • 调用本类方法
调用该类自己的方法,而没有经过spring的代理类,默认只有在外部调用事务才会生效
  • 异常被吃
 将异常try catch 没有throw出来。回滚异常没有,无法回滚
  • 异常类型错误
spring默认只会回滚非检查异常和error异常
如果是抛出受检异常,需要在注解@Transactional加属性rollbackFor
  • final修饰方法
因为spring事务是用动态代理实现,因此如果方法使用了final修饰,则代理类无法对目标方法进行重写,植入事务功能

====================

spring的事务是声明式事务,而声明式事务的本质是SpringAOP,SpringAOP的本质是动态代理。事务要生效必须代理对象在调用。

自调用问题

通过this调用同一个service中的方法,this是指service实现类对象本身,不是代理对象,就相当于方法中的代码粘到了大方法里面,相当于还是一个方法。(会导致事务失效)

@Override
@Transactional
public void bigSave(SpuVo spuVo) {
    /// 1.保存spu相关
    // 1.1. 保存spu基本信息 spu_info
    Long spuId=saveSpu();
    // 1.2. 保存spu的描述信息 spu_info_desc
    this.saveSpuDesc(spuVo, spuId);
    //1.3、保存spu的基本属性
    this.saveSpuBaseAttrs(spuVo, spuId);
    
    // 2. 保存sku相关信息
    this.saveSkus(spuVo, spuId);
    //给mq中发送消息
    this.sendMessage(spuId);
}

自调用问题-解决办法一

通过其他service对象(spuDescService)调用,这个service对象本质是动态代理对象

自调用问题-解决办法二

this.方法名()替换成this代理对象.方法名()即可。

在类中获取代理对象分三个步骤:

  1. 导入aop的场景依赖:spring-boot-starter-aop

  2. 开启AspectJ的自动代理,同时要暴露代理对象:@EnableAspectJAutoProxy(exposeProxy=true)

  3. 获取代理对象:SpuInfoService proxy = (SpuInfoService) AopContext.currentProxy();


 数据库连接池

1、数据库连接池

JDBC的数据库连接池使用 javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现;

数据库连接池(connection pool)概念:本质上是个集合容器,里面存放着数据库的连接。

  • 系统初始化时,创建一定数量的连接对象放入连接池。
  • 当有需要时,从连接池中获取空闲的连接对象,对数据库进行操作
  • 使用完毕后,将该连接对象归还至连接池,方便后续复用

数据库连接池的设计思想:消除频繁创建对象和释放资源带来的延迟,提高系统性能


2、Druid

Druid(德鲁伊)是Alibaba提供的数据库连接池,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。

Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

添加上 Druid 数据源依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>    
    <groupId>com.alibaba</groupId>   
    <artifactId>druid</artifactId>    
    <version>1.1.21</version>
</dependency>

切换数据源; Spring Boot 2.0 以上默认使用 com.zaxxer.hikari.HikariDataSource 数据源,但可以 通过 spring.datasource.type 指定数据源。

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource

设置Druid数据源连接初始化大小、最大连接数、等待时间、最小连接数 等设置项;

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3、Druid数据源监控

Druid是阿里巴巴开发的号称为监控而生的数据库连接池!

  • 充当数据库连接池。

  • 可以监控数据库访问性能

  • 获得SQL执行日志


4、Hikari

Spring Boot 2.0 以上默认使用 Hikari 数据源

spring-boot-starter-jdbc默认的数据库连接池是HikariCP

HikariCP是开源的一个数据库连接池组件,代码非常轻量,并且速度非常的快。

HiKariCP 号称业界跑得最快的数据库连接池,更是被 Spring Boot 2.0 选中作为其默认数据库连接池

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1054848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++八股

1、简述一下C中的多态 在面向对象中&#xff0c;多态是指通过基类的指针或引用&#xff0c;在运行时动态调用实际绑定对象函数的行为&#xff0c;与之相对应的编译时绑定函数称为静态绑定。 静态多态 静态多态是编译器在编译期间完成的&#xff0c;编译器会根据实参类型来选择…

国庆10.1

用select实现服务器并发 ser #include <myhead.h> #define ERR_MSG(msg) do{\fprintf(stderr, "__%d__", __LINE__);\perror(msg);\ }while(0)#define PORT 8888 //端口号&#xff0c;范围1024~49151 #define IP "192.168.1.205" //本机…

ARMv7-A 那些事 - 5.CP15协处理器

By: Ailson Jack Date: 2023.10.01 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/157.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

14:STM32-----看门狗

目录 一:看门狗 1:WDG 2:独立看门狗 (IWDG) A:IWDG框图 B:IWDG_KR键寄存器 C:IWDG超时时间 3:窗口看门狗 (WWDG) A:WWDG框图 B:WWDG工作特性 C:WWDG超时时间 4:独立看门狗和窗口看门狗的区别 5:数据手册 二:案例 A:独立看门狗 1:连接图 2:步骤 3:函数介绍 3:代…

网络爬虫——urllib(2)

前言&#x1f36d; ❤️❤️❤️网络爬虫专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Python网络爬虫_热爱编程的林兮的博客-CSDN博客 前篇讲解了urllib的基本使用、一个类型六个方法与下载相关内容&#xff0…

《深入浅出OCR》第二章:OCR技术发展与分类

✨专栏介绍: 经过几个月的精心筹备,本作者推出全新系列《深入浅出OCR》专栏,对标最全OCR教程,具体章节如导图所示,将分别从OCR技术发展、方向、概念、算法、论文、数据集等各种角度展开详细介绍。 👨‍💻面向对象: 本篇前言知识主要介绍深度学习知识,全面总结知知识…

Python3数据科学包系列(一):数据分析实战

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 认识下数据科学中数据处理基础包: (1)NumPy 俗话说: 要学会跑需先…

C++核心编程--多态篇

4.7、多态 4.7.1、多态的基本概念 多态是C面向对象三大特征之一 多态分为两类 静态多态&#xff1a;函数重载和运算符重载属于静态多态&#xff0c;复用函数名动态多态&#xff1a;派生类和虚函数实现运行时多态 静态多态和动态多态区别&#xff1a; 静态多态的函数地址早…

Eclipse 主网即将上线迎空投预期,Zepoch 节点或成受益者?

目前&#xff0c;Zepoch 节点空投页面中&#xff0c;模块化 Layer2 Rollup 项目 Eclipse 出现在其空投列表中。 配合近期 Eclipse 宣布了其将由 SVM 提供支持的 Layer2 主网架构&#xff0c;并将在今年年底上线主网的消息后&#xff0c;不免引发两点猜测&#xff1a;一个是 Ecl…

springcloud:四、nacos介绍+启动+服务分级存储模型/集群+NacosRule负载均衡

nacos介绍 nacos是阿里巴巴提供的SpringCloud的一个组件&#xff0c;算是eureka的替代品。 nacos启动 安装过程这里不再赘述&#xff0c;相关安装或启动的问题可以见我的另一篇博客&#xff1a; http://t.csdn.cn/tcQ76 单价模式启动命令&#xff1a;进入bin目录&#xff0…

某房产网站登录RSA加密分析

文章目录 1. 写在前面2. 抓包分析3. 扣加密代码4. 还原加密 1. 写在前面 今天是国庆节&#xff0c;首先祝福看到这篇文章的每一个人节日快乐&#xff01;假期会老的这些天一直在忙事情跟日常带娃&#xff0c;抽不出一点时间来写东西。夜深了、娃也睡了。最近湖南开始降温了&…

SimpleCG动画示例--汉诺塔动画演示

前言 SimpleCG的使用方法在前面已经介绍了许多&#xff0c;有兴趣的同学如果有去动手&#xff0c;制作一些简单动画应该没多大问题的。所以这次我们来演示一下简单动画。我们刚学习C语言的递归函数时&#xff0c;有一个经典例子相信很多同学都写过&#xff0c;那就是汉诺塔。那…

【算法优选】双指针专题——壹

文章目录 &#x1f60e;前言&#x1f334;[移动零](https://leetcode.cn/problems/move-zeroes/)&#x1f6a9;题⽬描述&#xff1a;&#x1f6a9;算法思路&#x1f6a9;算法流程&#x1f6a9;代码实现 &#x1f340;[复写零](https://leetcode.cn/problems/duplicate-zeros/)&…

深入浅出线程池

一、线程 1、什么是线程 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际 运作单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;一个进程中可以并发多个线程&#xff0c;每条线 程并行执行不同的任务。 2、如…

2023-10-01 LeetCode每日一题(买卖股票的最佳时机)

2023-10-01每日一题 一、题目编号 121. 买卖股票的最佳时机二、题目链接 点击跳转到题目位置 三、题目描述 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一…

Python Appium 安卓自动化测试 基本使用 - Phone Spider

Python Appium 安卓自动化测试 基本使用 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 Python Appium 安卓自动化测试 基本使用前言一、环境安装1.1 Python Pip…

【接口技术】输入输出接口习题

1&#xff1a;图1所示电路PC/XT系统板上的接口控制电路的端口地址译码电路。写出8237A&#xff0c;8259A&#xff0c;8253&#xff0c;8255A的端口地址范围 解答&#xff1a; 【G1A非】和【G2B非】为低电平有效&#xff0c;因此A80&#xff0c;A90 74ls138中&#xff0c;是按…

python代码混淆与代码打包

0x00 背景 自己写的项目&#xff0c;又想保护源码&#xff0c;自己做个混淆是最方便的了。 0x01 实践 这里使用开源工具 GitHub - astrand/pyobfuscate: pyobfuscate&#xff0c;虽然git上才500多star&#xff0c;但是很好用。它的使用场景是混淆单个py文件。很多事物有开始就…

六、vpp 流表

草稿&#xff01;&#xff01;&#xff01; vpp node其实就是三个部分 1、plugin init 2、set command 3、function 实现功能&#xff0c;比如这里的流表 这里的function函数有个返回值&#xff0c;根据返回值决定下一个节点走哪里 flowtable_getinfo这里处理函数返回2 &#…

Nodejs沙箱逃逸

一、基本概念 JavaScript和Nodejs之间有什么区别 JavaScript用在浏览器前端&#xff0c;后来将Chrome中的v8引擎单独拿出来为JavaScript单独开发了一个运行环境&#xff0c;因此JavaScript也可以作为一门后端语言&#xff0c;写在后端&#xff08;服务端&#xff09;的JavaScr…