SpringBoot⑨SpringData:JDBC、Druid数据源、MyBatis

10、Spring Data

Spring DataSpring 中与 Spring FrameworkSpring Boot 等齐名的项目。

对于数据访问层(包括 SQLNOSQL),Spring Boot 底层采用 Spring Data 进行处理。

10.1、JDBC

创建一个 Spring Boot 项目,用于测试 JDBC 的使用。

10.1.1、环境搭建

创建项目,勾选 JDBC 所需的依赖(也可以在pom.xml中导入依赖)

  • JDBC API
  • MySQL Driver

image-20211016163527009

查看依赖项

  • Spring 导入了以下依赖;
  • 在需要使用到 JDBCSpring Boot 项目中,直接导入以下两个依赖即可 ;

image-20211016171756137

10.1.2、数据库配置文件

resources 目录下,创建数据库配置文件application.yaml
(或application.properties

spring:
  datasource:
    username: root
    password: 密码
    # MySQL8.0之后需要设置时区,并且driver位于cj包下
    url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver

测试数据源

@Test
void contextLoads() throws SQLException {
    // 默认数据源
    System.out.println(dataSource.getClass());
    // 获取数据库连接
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    // 关闭连接
    connection.close();
}

image-20211016172455544

  1. 默认数据源:HikariDataSource

  2. 如果要自定义数据源,在配置文件中使用type属性

    spring:
      datasource:
        ...
    	type: 数据源
    

成功得到了数据源数据库连接,就可以使用原生 JDBC 来操作数据库。

10.1.3、JdbcTemplate

原生的 JDBC 比较麻烦,我们可以使用第三方持久层框架(如 MyBatis)。

Spring 也对 JDBC 做了轻量级的封装: JdbcTemplate,封装了 CRUD 方法,主要的几类方法如下:

  1. execute
    • 可以执行任何查询语句,一般用于执行 DDL 语句
  2. updatebatchUpdate
    • update:执行增删改语句
    • batchUpdate:执行批处理相关语句
  3. queryqueryForXxx
    • 执行查询语句
  4. call
    • 执行存储过程、函数语句

以上方法都有重载方法,常用的两个方法

  1. method(String sql)
    • 执行完整的 SQL 语句;
    • 如:SELECT * FROM user
  2. method(String sql, Object... args)
    • 执行带有占位符SQL
    • args:为占位符赋值,注意顺序。(类似 JDBC 中的 preparedStatement
    • 如:SELECT * FROM user WHERE id = ?Object[] agrs = {7}

10.1.4、CRUD

使用 JdbcTemplate 进行 CRUD

  1. UserDao

    • 由于没有实体类,用 map 来存储对象
    public interface UserDao {
    
        void insertUser();
    
        void deleteUser(Integer id);
    
        void updateUser(Integer id, String name);
    
        Map<String, Object> getUser(Integer id);
    
        List<Map<String, Object>> listUsers();
    }
    
  2. UserDaoImpl

    • @Repository:注册到 Spring 容器中;
    • update:增删改
    • queryXxx:查询
    @Repository
    public class UserDaoImpl implements UserDao {
    
        @Resource
        JdbcTemplate jdbcTemplate;
    
        @Override
        public void insertUser() {
            String sql = "INSERT INTO jdbcstudy.user(id, name, password) VALUES(10,'test','123456')";
            jdbcTemplate.update(sql);
        }
    
        @Override
        public void deleteUser(Integer id) {
            String sql = "DELETE FROM jdbcstudy.user WHERE id = " + id;
            jdbcTemplate.update(sql);
        }
    
        @Override
        public void updateUser(Integer id, String name) {
            String sql = "UPDATE jdbcstudy.user SET name = ? WHERE id = ?";
            Object[] params = {name, id};
            jdbcTemplate.update(sql, params);
        }
    
        @Override
        public Map<String, Object> getUser(Integer id) {
            String sql = "SELECT * FROM jdbcstudy.user WHERE id = ?";
            return jdbcTemplate.queryForMap(sql, id);
        }
    
        @Override
        public List<Map<String, Object>> listUsers() {
            String sql = "SELECT * FROM jdbcstudy.user";
            return jdbcTemplate.queryForList(sql);
        }
    }
    
  3. UserDaoTest

    • @AutowiredDI 注入;
    • 如果 UserDao 实现类 没有@Repository注册到容器中,此处无法注入成功
    @SpringBootTest
    public class UserDaoTest {
        @Autowired
        UserDao userDao;
    
        @Test
        void testInsertUser() {
            userDao.insertUser();
        }
    
        @Test
        void testDeleteUser() {
            userDao.deleteUser(10);
        }
    
        @Test
        void testUpdateUser() {
            userDao.updateUser(8);
        }
    
        @Test
        void testGetUser() {
            System.out.println(userDao.getUser(7));
        }
    
        @Test
        void testListUsers() {
            System.out.println(userDao.listUsers());
        }
    }
    

10.2、JDBC连接池:Druid

10.2.1、环境搭建

  1. 搭建 JDBC 环境:导入依赖

    <!-- JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- MySQL连接 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
  2. Druid 数据源

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

10.2.2、数据库配置文件

resource 目录下,创建数据库配置文件applicaion.yaml
(实际上是在使用 JDBC 的基础上,增加了一些配置)

spring:
  datasource:
    username: root
    password: 密码
    url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    # Druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    # Druid数据源配置
    initialSize: 5
    maxActive: 20
    minIdle: 5
    maxWait: 60000
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    validationQuery: SELECT 1 FROM DUAL
    testOnBorrow: false
    testOnReturn: false
    testWhileIdle: true
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    # filters:stat监控统计、log4j日志记录(需要导入log4j依赖)、wall防御sql注入
    filters: stat,wall,log4j
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

测试数据源

image-20211017205722375

  • 数据源变成了 DruidDataSource

  • 由于开启了 filters 中的 log4j,因此需要导入 log4j 的依赖

    <!-- log4j:注意是Apache Log4j,而不是Apache Log4j Core-->
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    

常用配置

配置 说明 默认值
name 数据源名称。
如果存在多个数据源,监控时通过名称区分
"DataSource-" + System.identityHashCode(this)
jdbcUrl 连接数据库的 url -
username 连接数据库的用户名 -
password 连接数据库的密码 -
driverClassName 数据库驱动名 Druid 根据 url 自动识别dbType
initialSize 初始化时建立物理连接的个数。
初始化发生在显式调用 init 方法,或第一次 getConnection
0
maxActive 最大连接数 8
maxIdle 最大空闲数,已弃用 8
minIdle 最小空闲数 -
maxWait 获取连接时最大等待时间(毫秒)。
配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
-
poolPreparedStatements 是否缓存 preparedStatement(即PSCache false
maxOpenPreparedStatements 要启用 PSCache,必须配置大于0。当大于0时 poolPreparedStatements 自动为 true -1
validationQuery 用来检测连接是否有效的 sql,要求是一个查询语句。
如果不配置,testOnBorrowtestOnReturntestWhileIdle 不起作用
-
testOnBorrow 申请连接时执行 validationQuery 检测连接是否有效,该配置会降低性能 true
testOnReturn 归还连接时执行 validationQuery 检测连接是否有效,该配置会降低性能 false
testWhileIdle 保证安全性,不影响性能,建议配置为 true
申请连接时检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效
false
timeBetweenEvictionRunsMillis 两个含义:
1) Destroy 线程会检测连接的间隔时间
2) testWhileIdle 的判断依据,详细看 testWhileIdle 属性的说明
1min
numTestsPerEvictionRun 已启用,一个 DruidDataSource 只支持一个 EvictionRun -
minEvictableIdleTimeMillis 连接保持空闲而不被驱逐的最长时间 30min
connectionInitSqls 物理连接初始化的时候执行的 sql -
exceptionSorter 当数据库抛出一些不可恢复的异常时,抛弃连接 根据 dbType 自动识别
filters 扩展插件
常用插件stat:监控统计、log4j:日志记录、wall:防御 sql 注入
-
proxyFilters filters 是组合关系,并非替换关系。
类型:List<com.alibaba.druid.filter.Filter
-

10.2.3、DruidConfig

自定义配置文件相当于在 Spring的 xml配置文件中配置 bean

DruidDataSource

  • 不使用 Spring Boot 自动创建的数据源,而是使用自定义 Druid 数据源;
  1. @Configuration:将配置类注册到容器中
  2. 绑定配置文件
    • @Bean自定义的数据源注入到容器中,“覆盖”掉 Spring Boot 自动创建的数据源;
    • @ConfigurationProperties(prefix = "")将全局配置文件中对应前缀的属性值,注入到自定义 Druid 数据源的同名参数中
@Configuration
public class DruidConfig {
    /**
     * 绑定配置文件
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }
    
    ...
}

stat 监控统计

  • @Bean:注册到容器中
  • ServletRegistrationBeanServlet 注册 Bean
  • 初始化参数:后台账号密码、允许访问、禁止访问名单
  • 设置初始化参数
@Configuration
public class DruidConfig {
	...

    @Bean
    public ServletRegistrationBean<StatViewServlet> statViewServlet() {
        // 访问/druid/下的任意路径,自动跳入监控统计的登录页
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");

        // 初始化参数
        HashMap<String, String> initParameters = new HashMap<>();
        // 用户名、密码:key是固定的
        initParameters.put("loginUsername", "admin");
        initParameters.put("loginPassword", "123456");
        // 允许访问名单:key为allow,value为空表示所有人
        initParameters.put("allow", "");
        // 禁止访问名单:key为禁止访问的人,value为ip地址
        // initParameters.put("someone","192.168.11.123");

        // 设置初始化参数
        bean.setInitParameters(initParameters);

        return bean;
    }
}

测试

  • 运行 SpringBoot 主程序,访问localhost:8080/druid,自动跳入登录页;

    image-20211017230407833

  • 只有正确输入了 loginUsernameloginPassword,才能进入后台

    image-20211017230526975

10.3、MyBatis

10.3.1、环境搭建

  1. 搭建 JDBC 环境:导入依赖

    <!-- JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- MySQL连接 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
  2. mybatis-spring-boot-starter

    <!-- SpringBoot整合MyBatis -->
    <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    

10.3.2、数据库配置文件

resource 目录下,创建数据库配置文件applicaion.yaml
(实际上是使用 JDBC 的数据库配置,有需要可以使用数据源)

spring:
  datasource:
    username: root
    password: 密码
    url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver

10.3.3、使用MyBatis

  1. 数据库表

    image-20211018111432037

  2. 实体类User

    public class User {
        /**
         * ID
         */
        private Integer id;
        /**
         * 用户名
         */
        private String name;
        /**
         * 密码
         */
        private String password;
        /**
         * 邮箱
         */
        private String email;
        /**
         * 生日
         */
        private Date birthday;
        
        ...
    }
    
  3. UserMapper

    @MapperMapper 扫描注册Spring 容器中,等价以下用法:

    • 注册:在 Mapper 使用@Repository
    • 扫描:在 Spring Boot 主启动类使用@MapperScan("Mapper所在包")
    @Mapper
    public interface UserMapper {
    
        void insertUser();
    
        void deleteUser(Integer id);
    
        void updateUser(Integer id, String name);
    
        User getUser(Integer id);
    
        List<User> listUsers();
    }
    
  4. MyBatis 配置

    • 在全局配置文件中配置,整合 MyBatis
    • 配置别名、注册 Mapper
    spring:
      ...
    
    mybatis:
      type-aliases-package: indi.jaywee.pojo
      # Mapper.xml放在resources目录下与mapper同名的包下
      mapper-locations: classpath:indi/jaywee/mapper/*.xml
    
  5. Mapper.xml

    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- 接口绑定 -->
    <mapper namespace="indi.jaywee.mapper.UserMapper">
        <!-- 添加用户 -->
        <insert id="insertUser">
            INSERT INTO jdbcstudy.user(id, name)
            VALUES ('20', 'somebody')
        </insert>
        
        <!-- 删除用户 -->
        <delete id="deleteUser">
            DELETE
            FROM jdbcstudy.user
            WHERE id = #{id}
        </delete>
    
        <!-- 更新用户 -->
        <update id="updateUser">
            UPDATE jdbcstudy.user
            SET name=#{name}
            WHERE id = #{id}
        </update>
    
        <!-- 根据ID查询用户 -->
        <select id="getUser" resultType="user">
            SELECT *
            FROM jdbcstudy.user
            WHERE id = #{id}
        </select>
    
        <!-- 查询所有用户-->
        <select id="listUsers" resultType="user">
            SELECT *
            from jdbcstudy.user
        </select>
    </mapper>
    

测试

@SpringBootTest
public class UserMapperTest {
    @Autowired
    UserMapper userMapper;

    @Test
    void testInsertUser() {
        userMapper.insertUser();
    }

    @Test
    void testDeleteUser() {
        userMapper.deleteUser(20);
    }

    @Test
    void testUpdateUser() {
        userMapper.updateUser(8, "eight");
    }

    @Test
    void testGetUser(){
        User user = userMapper.getUser(7);
        System.out.println(user);
    }

    @Test
    void testListUsers(){
        List<User> userList = userMapper.listUsers();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
posted @ 2021-10-17 22:19  Jaywee  阅读(189)  评论(0编辑  收藏  举报

👇