zoukankan      html  css  js  c++  java
  • 二、spring Boot构建的Web应用中,基于MySQL数据库的几种数据库连接方式进行介绍

     包括JDBC、JPA、MyBatis、多数据源和事务。

    一、JDBC 连接数据库

    1、属性配置文件(application.properties)

    spring.datasource.url=jdbc:mysql://localhost:3306/test
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver

    如果使用JNDI,则可以替代 spring.datasource 的 url、username、password,如:

    spring.datasource.jndi-name=java:tomcat/datasources/example 

    值得一提的是,无论是Spring Boot默认的DataSource配置还是你自己的DataSource bean,都会引用到外部属性文件中的属性配置。所以假设你自定义的DataSource bean,你可以在定义bean时设置属性,也可以在属性文件中,以“spring.datasource.*”的方式使属性配置外部化。

    2、pom.xml 配置maven依赖

            <!-- MYSQL -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
             </dependency>
             <!-- Spring Boot JDBC -->
             <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
              </dependency>

    3、Java代码范例

    StudentService.java

    package org.springboot.sample.service;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.springboot.sample.entity.Student;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    import org.springframework.stereotype.Service;
    
     
    @Service
    public class StudentService {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public List<Student> getList(){
            String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE   FROM STUDENT";
            return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){
    
                @Override
                public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Student stu = new Student();
                    stu.setId(rs.getInt("ID"));
                    stu.setAge(rs.getInt("AGE"));
                    stu.setName(rs.getString("NAME"));
                    stu.setSumScore(rs.getString("SCORE_SUM"));
                    stu.setAvgScore(rs.getString("SCORE_AVG"));
                    return stu;
                }
    
            });
        }
    }

    Student.java 实体类

    package org.springboot.sample.entity;
    
    import java.io.Serializable;
    
    public class Student implements Serializable{
    
        private static final long serialVersionUID = 2120869894112984147L;
    
        private int id;
        private String name;
        private String sumScore;
        private String avgScore;
        private int age;
    
        // 节省文章长度,get set 方法省略
    }

    StudentController.java

    package org.springboot.sample.controller;
    
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springboot.sample.entity.Student;
    import org.springboot.sample.service.StudentService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/stu")
    public class StudentController {
    
        private static final Logger logger = LoggerFactory.getLogger(StudentController.class);
    
        @Autowired
        private StudentService studentService;
    
        @RequestMapping("/list")
        public List<Student> getStus(){
            logger.info("从数据库读取Student集合");
            return studentService.getList();
        }
    }

    本文对工程添加文件后工程结构图: 
    这里写图片描述

    然后启动项目,访问地址: http://localhost:8080/myspringboot/stu/list 响应结果如下:

    [
    {
    id: 1,
    name: "小明",
    sumScore: "252",
    avgScore: "84",
    age: 1
    },
    {
    id: 2,
    name: "小王",
    sumScore: "187",
    avgScore: "62.3",
    age: 1
    },
    {
    id: 3,
    name: "莉莉",
    sumScore: "",
    avgScore: "",
    age: 0
    },
    {
    id: 4,
    name: "柱子",
    sumScore: "230",
    avgScore: "76.7",
    age: 1
    },
    {
    id: 5,
    name: "大毛",
    sumScore: "",
    avgScore: "",
    age: 0
    },
    {
    id: 6,
    name: "亮子",
    sumScore: "0",
    avgScore: "0",
    age: 1
    }
    

    连接池说明

    Tomcat7之前,Tomcat本质应用了DBCP连接池技术来实现的JDBC数据源,但在Tomcat7之后,Tomcat提供了新的JDBC连接池方案,作为DBCP的替换或备选方案,解决了许多之前使用DBCP的不利之处,并提高了性能。详细请参考:http://wiki.jikexueyuan.com/project/tomcat/tomcat-jdbc-pool.html

    Spring Boot为我们准备了最佳的数据库连接池方案,只需要在属性文件(例如application.properties)中配置需要的连接池参数即可。 
    我们使用Tomcat数据源连接池,需要依赖tomcat-jdbc,只要应用中添加了spring-boot-starter-jdbc 或 spring-boot-starter-data-jpa依赖,则无需担心这点,因为将会自动添加 tomcat-jdbc 依赖。 
    假如我们想用其他方式的连接池技术,只要配置自己的DataSource bean,即可覆盖Spring Boot的自动配置。

    请看我的数据源配置:

    spring.datasource.url=jdbc:mysql://localhost:3306/test
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    
    spring.datasource.max-idle=10
    spring.datasource.max-wait=10000
    spring.datasource.min-idle=5
    spring.datasource.initial-size=5
    spring.datasource.validation-query=SELECT 1
    spring.datasource.test-on-borrow=false
    spring.datasource.test-while-idle=true
    spring.datasource.time-between-eviction-runs-millis=18800
    spring.datasource.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=0)

    配置过连接池的开发人员对这些属性的意义都有所认识。

    我们打开DEBUG日志输出,logback.xml 中添加:

    <logger name="org.springframework.boot" level="DEBUG"/>

    然后启动项目,注意观察日志输出,如下图中会显示自动启用了连接池: 
    这里写图片描述 
    在上面的数据源配置中添加了过滤器,并设置了延迟时间为0(故意设置很低,实际项目中请修改):

    spring.datasource.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=0)

    这个时候,我们访问 http://localhost:8080/myspringboot/stu/list 观察日志,会发现框架自动将大于该时间的数据查询进行警告输出,如下:

     o.a.t.j.p.interceptor.SlowQueryReport    : Slow Query Report SQL=SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE   FROM STUDENT; time=3 ms;

    二、将介绍如何在spring Boot 工程中添加JPA作为持久化方式

    修改 pom.xml 依赖

    与上一篇介绍的 jdbc 不同的是 spring-boot-starter-jdbc 修改为 spring-boot-starter-data-jpa 即可,当然数据库驱动包也是不可少的,如下:

     <!-- MYSQL -->
     <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
     </dependency>
     <!-- Spring Boot JPA -->
     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
     </dependency>

    注意:如果你想JDBC和JPA可以一起使用,Spring Boot 是支持的,你只需要把JDBC和JPA的依赖都添加在pom.xml 中即可。无需其他特殊处理,有关JDBC的使用介绍请看上一篇 “Spring-Boot JDBC 连接数据库”。

    修改属性配置文件

    在属性配置文件中添加 JPA 相关属性,注意这些并非必须,我们如果只添加dataSource 的 urlusernamepassworddriver-class-name 也可以正常使用,有关JPA的其他配置都是可选的。

    spring.jpa.database=
    spring.jpa.show-sql=
    spring.jpa.properties=
    spring.jpa.generate-ddl=
    spring.jpa.open-in-view=
    spring.jpa.database-platform=
    spring.jpa.hibernate.ddl-auto=
    spring.data.jpa.repositories.enabled=
    spring.jpa.hibernate.naming-strategy=

    熟悉JPA的根据名字应基本知道这些分别的作用了。

    传统上,JPA实体类在persistence.xml文件中指定的。使用Spring Boot,这个文件是没有必要的,因为它使用“实体扫描”,默认情况下主配置 @EnableAutoConfiguration 或 @SpringBootApplication 下面的所有包都将会被扫描。任何使用注解 @Entity, @Embeddable 或 @MappedSuperclass 的类都将被管理。

    Java代码实例

    • 一个接口
    • 一个Controller

    我们创建一个接口 IScoreDao.Java ,然后我们继承框架为我们提供好的接口 Repository 或 CrudRepository (CrudRepository 继承自 Repository),其中为我们提供了对数据库的基本操作方法。

    package org.springboot.sample.dao;
    
    import java.util.List;
    
    import javax.transaction.Transactional;
    
    import org.springboot.sample.entity.Score;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.CrudRepository;
    import org.springframework.data.repository.query.Param;
    
    public interface IScoreDao extends CrudRepository<Score, Integer> {
    
        @Transactional
        @Modifying
        @Query("update Score t set t.score = :score where t.id = :id")
        int updateScoreById(@Param("score") float score, @Param("id") int id);
    
        @Query("select t from Score t ")
        List<Score> getList();
    
    }

    注意,如果你其中使用了修改、新增、删除操作,则必须要在接口上面或者对应的方法上面添加 @Transactional 注解,否则会抛出异常。

    实体类 Score.java

    package org.springboot.sample.entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    /**
     * 成绩
     *
     */
    @Entity
    @Table(name = "score")
    public class Score implements Serializable {
    
        private static final long serialVersionUID = 8127035730921338189L;
    
        @Id
        @GeneratedValue
        private int id;
    
        @Column(nullable = false, name="STUDENTID") // 这里说一下,我使用指定数据库列的时候,使用小写会不起作用,修改为大写便正常了。不知道为何,如果遇到一样问题的可以尝试下。
        private int stuId;
    
        @Column(nullable = false, name="SUBJECTNAME")
        private String subjectName;
    
        @Column(nullable = false)
        private float score;
    
        @Column(nullable = false, name="EXAMTIME")
        private Date examTime; 
    
        // 省去get、set 方法(占用文章空间)
    
    }

    ScoreController.java

    package org.springboot.sample.controller;
    
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springboot.sample.dao.IScoreDao;
    import org.springboot.sample.entity.Score;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/score")
    public class ScoreController {
    
        private static final Logger logger = LoggerFactory.getLogger(ScoreController.class);
    
        @Autowired
        private IScoreDao scoreService;
    
        @RequestMapping("/scoreList")
        public List<Score> getScoreList(){
            logger.info("从数据库读取Score集合");
            // 测试更新数据库
            logger.info("更新的行数:" + scoreService.updateScoreById(88.8f, 2));
            scoreService.delete(23);
    
            return scoreService.getList();
        }
    }

    然后在浏览器访问地址:http://localhost:8080/myspringboot/score/scoreList 测试


    最后要说明的是,Spring 会自动为我们继承CrudRepository接口的接口创建实现类。我们只需要在使用的时候直接使用注解 @Autowired 注入即可(IScoreDao 接口上也没有必要增加 @Component 、 @Repository 等注解)。 
    还有,我这里为了简单起见,直接将操作数据库的在Controller中使用,实际项目中,是不建议这样做的,IScoreDao 的所属角色是数据库持久,我们还应当有 Service(如ScoreService) 来调用 IScoreDao 的方法,然后在Controller 中调用 Service 中的方法。原因是因为,我们数据库访问层,都是接口定义方法,上面注解注入SQL和参数,没有具体的代码逻辑处理。如果我们想在执行SQL之前或之后执行逻辑处理,只能在 Service 中或者Controller(不建议)中。 
    我们严格按照这种方式去做(持久层只与SQL有关,通过接口定义无逻辑处理),这样才是彻彻底底的持久层。越严格的规范制度,在某种程度上来说其实越有利于代码的管理和项目代码的迭代发展。

    当然,如果你实在想要实现自己的 class 实现类,下面会附上一个实例代码,在此之前,我们先看一个图片: 
    这里写图片描述

    这个图是Spring 使用动态代理创建的接口实例,可以看出,其使用的是 SimpleJpaRepository 类,所以如果我们实现自己的 Repository ,可以扩展 SimpleJpaRepository 并 实现自己的 factory-class 入手,这里不做详解。注意凡事实现 Repository 接口的实现类都不需要添加 @Repository 注解,否则你会遇到问题。

    本文介绍JPA 相比上一篇关于JDBC 的介绍增加的文件工程截图为: 
    工程结构截图

    熟悉其中一种持久数据的方法后,其他类似的都大同小异。

    三、SpringBoot种配置MyBatis

    其实mybatis官网在2015年11月底就已经发布了对SpringBoot集成的Release版本,Github上有代码:https://github.com/mybatis/mybatis-spring-boot 
    前面对JPA和JDBC连接数据库做了说明,本文也是参考官方的代码做个总结。

    先说个题外话,SpringBoot默认使用 org.apache.tomcat.jdbc.pool.DataSource 
    现在有个叫 HikariCP 的JDBC连接池组件,据说其性能比常用的 c3p0、tomcat、bone、vibur 这些要高很多。 
    我打算把工程中的DataSource变更为HirakiDataSource,做法很简单: 
    首先在application.properties配置文件中指定dataSourceType

    spring.datasource.type=com.zaxxer.hikari.HikariDataSource

    然后在pom中添加Hikari的依赖

    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <!-- 版本号可以不用指定,Spring Boot会选用合适的版本 -->
    </dependency>

    言归正传,下面说在spring Boot中配置MyBatis。 
    关于在Spring Boot中集成MyBatis,可以选用基于注解的方式,也可以选择xml文件配置的方式。通过对两者进行实际的使用,还是建议使用XML的方式(官方也建议使用XML)。

    下面将介绍通过xml的方式来实现查询,其次会简单说一下注解方式,最后会附上分页插件(PageHelper)的集成。

    一、通过xml配置文件方式

    1、添加pom依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <!-- 请不要使用1.0.0版本,因为还不支持拦截器插件,1.0.1-SNAPSHOT 是博主写帖子时候的版本,大家使用最新版本即可 -->
        <version>1.0.1-SNAPSHOT</version>
    </dependency>

    2、创建接口Mapper(不是类)和对应的Mapper.xml文件

    定义相关方法,注意方法名称要和Mapper.xml文件中的id一致,这样会自动对应上 
    StudentMapper.Java

    package org.springboot.sample.mapper;
    
    import java.util.List;
    
    import org.springboot.sample.entity.Student;
    
    /**
     * StudentMapper,映射SQL语句的接口,无逻辑实现
     *
     */
    public interface StudentMapper extends MyMapper<Student> {
    
        List<Student> likeName(String name);
    
        Student getById(int id);
    
        String getNameById(int id);
    
    }

    MyMapper.java

    package org.springboot.sample.config.mybatis;
    
    import tk.mybatis.mapper.common.Mapper;
    import tk.mybatis.mapper.common.MySqlMapper;
    
    /**
     * 被继承的Mapper,一般业务Mapper继承它
     *
     */
    public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
        //TODO
        //FIXME 特别注意,该接口不能被扫描到,否则会出错
    }

    StudentMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="org.springboot.sample.mapper.StudentMapper">
    
        <!-- type为实体类Student,包名已经配置,可以直接写类名 -->
        <resultMap id="stuMap" type="Student">
            <id property="id" column="id" />
            <result property="name" column="name" />
            <result property="sumScore" column="score_sum" />
            <result property="avgScore" column="score_avg" />
            <result property="age" column="age" />
        </resultMap>
    
        <select id="getById" resultMap="stuMap" resultType="Student">
            SELECT *
            FROM STUDENT
            WHERE ID = #{id}
        </select>
    
        <select id="likeName" resultMap="stuMap" parameterType="string" resultType="list">
            SELECT *
            FROM STUDENT
            WHERE NAME LIKE CONCAT('%',#{name},'%')
        </select>
    
        <select id="getNameById" resultType="string">
            SELECT NAME
            FROM STUDENT
            WHERE ID = #{id}
        </select>
    
    
    </mapper> 

    3、实体类

    package org.springboot.sample.entity;
    
    import java.io.Serializable;
    
    /**
     * 学生实体
     *
     */
    public class Student implements Serializable{
    
        private static final long serialVersionUID = 2120869894112984147L;
    
        private int id;
        private String name;
        private String sumScore;
        private String avgScore;
        private int age;
    
        // get set 方法省略
    
    }

    4、修改application.properties 配置文件

    mybatis.mapper-locations=classpath*:org/springboot/sample/mapper/sql/mysql/*Mapper.xml
    mybatis.type-aliases-package=org.springboot.sample.entity

    5、在Controller或Service调用方法测试

    
        @Autowired
        private StudentMapper stuMapper;
    
        @RequestMapping("/likeName")
        public List<Student> likeName(@RequestParam String name){
            return stuMapper.likeName(name);
        }

    二、使用注解方式

    查看官方Git上的代码使用注解方式,配置上很简单,使用上要对注解多做了解。至于xml和注解这两种哪种方法好,众口难调还是要看每个人吧。

    1、启动类(我的)中添加@MapperScan注解

    @SpringBootApplication
    @MapperScan("sample.mybatis.mapper")
    public class SampleMybatisApplication implements CommandLineRunner {
    
        @Autowired
        private CityMapper cityMapper;
    
        public static void main(String[] args) {
            SpringApplication.run(SampleMybatisApplication.class, args);
        }
    
        @Override
        public void run(String... args) throws Exception {
            System.out.println(this.cityMapper.findByState("CA"));
        }
    
    }

    2、在接口上使用注解定义CRUD语句

    package sample.mybatis.mapper;
    
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    
    import sample.mybatis.domain.City;
    
    /**
     * @author Eddú Meléndez
     */
    public interface CityMapper {
    
        @Select("SELECT * FROM CITY WHERE state = #{state}")
        City findByState(@Param("state") String state);
    
    }

    其中City就是一个普通Java类。 
    关于MyBatis的注解,有篇文章讲的很清楚,可以参考: http://blog.csdn.net/luanlouis/article/details/35780175

    三、集成分页插件

    这里与其说集成分页插件,不如说是介绍如何集成一个plugin。MyBatis提供了拦截器接口,我们可以实现自己的拦截器,将其作为一个plugin装入到SqlSessionFactory中。 
    Github上有位开发者写了一个分页插件,我觉得使用起来还可以,挺方便的。 
    Github项目地址: https://github.com/pagehelper/Mybatis-PageHelper

    下面简单介绍下: 
    首先要说的是,Spring在依赖注入bean的时候,会把所有实现MyBatis中Interceptor接口的所有类都注入到SqlSessionFactory中,作为plugin存在。既然如此,我们集成一个plugin便很简单了,只需要使用@Bean创建PageHelper对象即可。

    1、添加pom依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>4.1.0</version>
    </dependency>

    2、新增MyBatisConfiguration.java

    package org.springboot.sample.config;
    
    import java.util.Properties;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.github.pagehelper.PageHelper;
    
    /**
     * MyBatis 配置
     *
     */
    @Configuration
    public class MyBatisConfiguration {
    
        private static final Logger logger = LoggerFactory.getLogger(MyBatisConfiguration.class);
    
        @Bean
        public PageHelper pageHelper() {
            logger.info("注册MyBatis分页插件PageHelper");
            PageHelper pageHelper = new PageHelper();
            Properties p = new Properties();
            p.setProperty("offsetAsPageNum", "true");
            p.setProperty("rowBoundsWithCount", "true");
            p.setProperty("reasonable", "true");
            pageHelper.setProperties(p);
            return pageHelper;
        }
    
    }

    3、分页查询测试

        @RequestMapping("/likeName")
        public List<Student> likeName(@RequestParam String name){
            PageHelper.startPage(1, 1);
            return stuMapper.likeName(name);
        }

    更多参数使用方法,详见PageHelper说明文档(上面的Github地址)。

     
  • 相关阅读:
    Git详解之四 服务器上的Git
    Git详解之三 Git分支
    Git详解之二 Git基础
    Git详解之一 Git起步
    Spring Data JPA 梳理
    Spring Data JPA 梳理
    Spring boot 梳理
    Apache和Tomcat 配置负载均衡(mod-proxy方式)-粘性session
    Apache和Tomcat 配置负载均衡(mod-proxy方式)-无session共享、无粘性session
    Spring boot 官网学习笔记
  • 原文地址:https://www.cnblogs.com/chenliangcl/p/7345847.html
Copyright © 2011-2022 走看看