[Spring Boot 2]整合持久层技术

持久层技术,简单来说,就是通过操纵对象来操作数据库。(具体见前言部分)

本期博客继续深入Spring Boot技术,值得一提的是,尽管Spring Boot操作起来还是不错的,看都看明白,但还是强烈建议大家自己动手操作,里面的坑还是很多的,只有经历了自己才知道。一般来说,像Web项目不会精确定位是哪一段代码有什么问题,只会告诉你有一些逻辑问题,大多我在Java里面遇到的就是NULL空指针类型的,寻找错误起来异常麻烦,很多时候都是一个细节需要找很久。(所以真的,Code必须要仔细啊)

本期的数据库操作主要围绕着JdbcTemplate ,MyBatis,Spring Data JPA,不讲JDBC了(第一个也都是使用数据连接池来操作的)

后期的博客主要是整合NoSQL,构建RESTful服务,单元测试,安全管理等,持续更新!(不过我主要还是以后端为主,前端能力依旧差很多,需要在短期补足一下)

前言

持久层,可以理解成数据 保存在 数据库或者 硬盘一类可以保存很长时间的设备里面,不像放在内存中那样断电就消失了,也就是把数据存在持久化设备上。
持久化可以通过很多方式,写文件和数据库都可以。只是现在企业一般都会选择把数据持久化到数据库中,因为可以很方便的查询统计分析,但数据库的数据最终还是会写到磁盘上的。Java 程序员为了操作数据库, 最开始是使用JDBC* 来进行的,但是这种方式* 开发效率低 ,要写一堆重复代码,加上关系数据库和对象本身存在所谓的阻抗不匹配情况,所以 为了提高开发效率,有人发明了 ORM 即 对象关系映射框架* (Hibernate是其中的佼佼者),对于 Java 程序员来说,就可以通过操纵对象来操纵数据库了。
持久层是Java EE中访问数据库的核心操作,Spring Boot对常见的持久层框架提供了自动化配置,例如JdbcTemplate,JPA等,MyBatis的自动化配置是MyBatis官方提供的。
其中,JdbcTemplate使用得不是很广泛,MyBatis灵活性较好,方便开发者进行SQL优化。Spring Data JPA使用方便,能快速实现一共RESTful风格的应用。
一般构建目录如下:分为Controller,dao,model,service层相互应用。
在这里插入图片描述

整合JdbcTemplate

JdbcTemplate是Spring提供的JDBC模板框架,利用AOP技术来解决使用JDBC时大量重复代码问题。虽没有MyBatis那么灵活但是方便很多。
这里都是以数据连接池为例。

下面以一个实例为例:
建表命令:

create database  chapter05 default character set utf8;
use chapter05;
create table book(
    **id int(11) not null  auto_increment,**
    name varchar(128) default null,
    author varchar(64) default null,
    primary key (id)
);
insert into book (id, name, author) value (1,'ABC','ACD');

添加依赖:
spring-boot-jdbc提供了spring-jdbc ,例外加入了数据库驱动依赖和数据库连接池依赖。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.2</version>
        </dependency>

数据库配置:
在application.properties配置数据库连接信息:记得配置完依赖需要刷新依赖。

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///chapter05
spring.datasource.username = root
spring.datasource.password = qazwsx123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver // 下面版本号为8.0.30

创建book的实体:

public class Book {
    private   Integer id;
    private   String name;
    private  String author;
    @JsonFormat(pattern = "yyyy-MM-dd")
    public  Integer getId() {
        return id;
    }
    public  String getName() {
        return name;
    }
    public String getAuthor() {
        return author;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public  void setAuthor(String author) {
        this.author = author;
    }
}

创建BookDao,创建数据库访问层:

@Repository
public class BookDao {
    @Autowired(
            required = false
    )
    JdbcTemplate jdbcTemplate;
    public int addBook(Book book) {
        return jdbcTemplate.update("insert into book(name ,author) values (?,?)",book.getName(),book.getAuthor());
    }
    public int updateBook(Book book) {
        return jdbcTemplate.update("update book set name=?,author=? where id=?",book.getName(),book.getAuthor(),book.getId());
    }
    public int deleteBookById(Integer id) {
        return jdbcTemplate.update("delete from  book where id =? ",id);
    }
    public Book getBookById(Integer id) {
        return jdbcTemplate.queryForObject("select * from book where id = ?",new BeanPropertyRowMapper<>(Book.class),id);
    }
    public List<Book> getAllBooks() {
        return jdbcTemplate.query("select * from book",new BeanPropertyRowMapper<>(Book.class));
    }
}

在我没有对AUTOWIRED进行配置的时候,出现了这个错误:Could not autowire. No beans of ‘JdbcTemplate’ type found.
所以需要加上false语句来降低安全性
解释:
注入JdbcTemplate,由于添加了spring-jdbc相关依赖,所以会自动注册到Spring容器,因此可以直接注入JdbcTemplate使用。
增删改三种类型操作主要使用update,batchUpdate来完成。query和queryForObject主要完成查询功能。还有execute来执行任意的SQL,call方法。
在执行查询操作,需要一个RowMapper将查询的列和实体相对应。如果列名和属性名相同,那么可以直接使用BeanPropertyRowMapper,如果列名和属性不同,就需要开发者自己实现RowMapper接口。

创建Service

@Service
public class BookService {

    @Autowired(
            required = false
    )
    BookDao bookDao;
    public int addBook(Book book) {
        System.out.println(book.getId());
        System.out.println(book.getAuthor());
        System.out.println(book.getName()+"1233");

        return bookDao.addBook(book);
    }
    public int updateBook(Book book) {
        return bookDao.updateBook(book);
    }
    public  int deleteBookById(Integer id) {
        return bookDao.deleteBookById(id);
    }
    public Book getBookById(Integer id) {
        return bookDao.getBookById(id);
    }
    public List<Book> getAllBooks() {
        return bookDao.getAllBooks();
    }
}

Controller:

@RestController
public class BookController {
    @Autowired
    BookService bookService;
    @GetMapping("/bookOps")
    public void bookOps() {
        Book b1 = new Book();
        b1.setId(4);
        b1.setName("西厢记");
        b1.setAuthor("王实甫");
        int i = bookService.addBook(b1);
        System.out.println("addBook>>>" + i);
        Book b2 = new Book();
        b2.setId(1);
        b2.setName("朝花夕拾");
        b2.setAuthor("鲁迅");
        int updateBook = bookService.updateBook(b2);
        System.out.println("updateBook>>>"+updateBook);
        Book b3 = bookService.getBookById(1);
        System.out.println("getBookById>>>"+b3);
        int delete = bookService.deleteBookById(2);
        System.out.println("deleteBookById>>>"+delete);
        List<Book> allBooks = bookService.getAllBooks();
        System.out.println("getAllBooks>>>"+allBooks);
    }
}

在这里我遇到了很多坑,首先是初始化问题。记住一定要把类的创建放在autowired(自动装箱)后面,否则一直会报错null.

整合MyBatis

MyBatis是一款优秀的持久层框架,支持定制化SQL,存储过程以及高级映射。MyBatis几乎避免所有的JDBC代码手动设置参数以及获取结果集。在传统的SSM框架整合中,使用它需要大量的配置,而在Spring Boot提供了自动化配置方案,可以做到开箱即用。步骤如下:

首先展现项目文件目录情况:
在这里插入图片描述

添加依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

数据库和表,实体类以及application.properties配置见上文jdbcTemplate一节。
创建数据库访问层
BookMapper:

@Mapper
public interface BookMapper {
    int addBook(Book book);
    int deleteBookById(Integer id);
    int updateBookById(Book book);
    Book getBookById(Integer id);
    List<Book> getAllBooks();
}

@Mapper注解,表示这是MyBatis中的接口,这种方式在每个Mapper添加注解。
BookMapper.xml:
BookMapper接口中的每一个方法都在BookMapper.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 ="com.example.demo.testMybatis.mapper.BookMapper">
    <insert id ="addBook" parameterType="com.example.demo.testMybatis.model.Book">
        insert into book (name,author) values (#{name},#{author})
    </insert>
    <delete id="deleteBookById" parameterType="int">
        delete from book where id = #{id}
    </delete>
    <update id="updateBookById" parameterType="com.example.demo.testMybatis.model.Book">
        update book set name = #{name},author = #{author} where id = #{id}
    </update>
    <select id="getBookById" parameterType="int" resultType="com.example.demo.testMybatis.model.Book">
        select  * from book where id = #{id}
    </select>
    <select id="getAllBooks" resultType="com.example.demo.testMybatis.model.Book">
        select * from book
    </select>
</mapper>

BookService.java:

@Service
public class BookService {
    @Autowired(
            required = false
    )
    BookMapper bookMapper;
    public int addBook(Book book) {
        return bookMapper.addBook(book);
    }
    public int updateBook(Book book) {
        return bookMapper.updateBookById(book);
    }
    public int deleteBookById(Integer id) {
        return bookMapper.deleteBookById(id);
    }
    public Book getBookById(Integer id) {
        return bookMapper.getBookById(id);
    }
    public List<Book> getAllBooks() {
        return bookMapper.getAllBooks();
    }
}

BookController.java:

@RestController
public class BookController {
    @Autowired
    BookService bookService;
    @GetMapping("/bookOps")
    public void bookOps() {
        Book b1 = new Book();
        b1.setName("西厢记");
        b1.setAuthor("王实甫");
        int i = bookService.addBook(b1);
        System.out.println("addBook>>>" + i);
        Book b2 = new Book();
        b2.setId(1);
        b2.setName("朝花夕拾");
        b2.setAuthor("鲁迅");
        int updateBook = bookService.updateBook(b2);
        System.out.println("updateBook>>>"+updateBook);
        Book b3 = bookService.getBookById(1);
        System.out.println("getBookById>>>"+b3);
        int delete = bookService.deleteBookById(2);
        System.out.println("deleteBookById>>>"+delete);
        List<Book> allBooks = bookService.getAllBooks();
        System.out.println("getAllBooks>>>"+allBooks);
    }
}

配置pom.xml文件:
在Maven工程中,xml配置文件建议写在resources目录,但我们的xml是写在了Mapper包下,故需要指明资源文件位置:

<build>
<resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
        </build>

当然可以把xml放在resources包中,并把文件夹名改为mapper,然后在application.properties配置:

# 配置mybatis
mybatis.mapper-locations=classpath:/mapper/*.xml

输入8080/bookOps,便可以在控制台打印出日志:
在这里插入图片描述

整合Spring Data JPA

JPA是一种ORM规范。Spring Data 是Spring的一个子项目,致力于简化数据库访问。通过规范的方法名称来分析开发者的意图,进而减少数据库访问层的代码量。Spring Data 不仅支持关系型数据库,也支持非关系型数据库。
Spring Boot 整合Spring Data JPA的步骤如下:

先建一个数据库:

create database  jpa default character set utf8;

创建数据库即可不用建表。
添加依赖:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

数据库配置:在application.properties配置数据库基本信息
1-4行是数据库基本信息配置,5-8行是JPA相关配置。第5行表示是否在控制台打印生成的SQL。第6行表示对JPA对应的数据库为MySQL,第7行表示项目启动时实时更新数据库中的表。第8行表示使用的数据库方言。

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///jpa
spring.datasource.username=root
spring.datasource.password=qazwsx123
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect

创建实体:
Book.java:

import javax.persistence.*;

@Entity(name ="jpabook")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "book_name",nullable = false)
    private String name;
    private String author;
    private Float price;
    @Transient
    private String description;
    //省略getter/setter

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                ", description='" + description + '\'' +
                '}';
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }
}

@Entity注解表示该类是一个实体类,项目启动时生成一张表,表的名称就是name值。(如果不配置name,默认为类名)
所有的实体类都要有主键,@Id表示该属性是一个主键,@GeneratedValue表示主键自动生成,strategy表示主键的生成策略。
默认情况下,生成的表字段名称就是实体类的属性。
@Transient注解表示在生成数据库表的时候不生成对应的字段。
创建BookDao接口:

BookDao.java:

public interface BookDao extends JpaRepository<Book,Integer> {
    List<Book> getBooksByAuthorStartingWith(String author);
    List<Book> getBooksByPriceGreaterThan(Float price);
    @Query(value = "select * from t_book where id=(select max(id) from t_book)",nativeQuery = true)
    Book getMaxIdBook();
    @Query("select b from book b where b.id>:id and b.author=:author")
    List<Book> getBookByIdAndAuthor(@Param("author") String author, @Param("id") Integer id);
    @Query("select b from book b where b.id<?2 and b.name like %?1%")
    List<Book> getBooksByIdAndName(String name, Integer id);
}

自定义BookDao继承自JpaRepository.其提供了一些基本的数据操作方法,有基本的增删改查、分页查询、排序查询等。
在JPA中,只要方法的定义符合既定规范,Spring Data就能分析出开发者的意图,从而避免开发者定义SQL。(既定规范,就是一定的方法命名规则,支持的命名规则如下:)
在这里插入图片描述

创建BookService:

@Service
public class BookService {
    @Autowired
    BookDao bookDao;
    public void addBook(Book book) {
        bookDao.save(book);
    }
    public Page<Book> getBookByPage(Pageable pageable) {
        return bookDao.findAll(pageable);
    }
    public List<Book> getBooksByAuthorStartingWith(String author){
        return bookDao.getBooksByAuthorStartingWith(author);
    }
    public List<Book> getBooksByPriceGreaterThan(Float price){
        return bookDao.getBooksByPriceGreaterThan(price);
    }
    public Book getMaxIdBook(){
        return bookDao.getMaxIdBook();
    }
    public List<Book> getBookByIdAndAuthor(String author, Integer id){
        return bookDao.getBookByIdAndAuthor(author, id);
    }
    public List<Book> getBooksByIdAndName(String name, Integer id){
        return bookDao.getBooksByIdAndName(name, id);
    }
}

在addBook中save方法将对象数据保存到数据库,save方法是由JpaRepository接口提供的。
在Page 是一个分页查询,使用findAll。

创建BookController,实现对数据的测试:

@RestController
public class BookController {
    @Autowired
    BookService bookService;
    @GetMapping("/findAll")
    public void findAll() {
        PageRequest pageable = PageRequest.of(2, 3);
        Page<Book> page = bookService.getBookByPage(pageable);
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println("总记录数:"+page.getTotalElements());
        System.out.println("查询结果:"+page.getContent());
        System.out.println("当前页数:"+(page.getNumber()+1));
        System.out.println("当前页记录数:"+page.getNumberOfElements());
        System.out.println("每页记录数:"+page.getSize());
    }
    @GetMapping("/search")
    public void search() {
        List<Book> bs1 = bookService.getBookByIdAndAuthor("鲁迅", 7);
        List<Book> bs2 = bookService.getBooksByAuthorStartingWith("吴");
        List<Book> bs3 = bookService.getBooksByIdAndName("西", 8);
        List<Book> bs4 = bookService.getBooksByPriceGreaterThan(30F);
        Book b = bookService.getMaxIdBook();
        System.out.println("bs1:"+bs1);
        System.out.println("bs2:"+bs2);
        System.out.println("bs3:"+bs3);
        System.out.println("bs4:"+bs4);
        System.out.println("b:"+b);
    }
    @GetMapping("/save")
    public void save() {
        Book book = new Book();
        book.setAuthor("鲁迅");
        book.setName("呐喊");
        book.setPrice(23F);
        bookService.addBook(book);
    }
}

调用8080/save,数据库就用显示相应的数据,同时使用8080/search查看数据。

多数据源

所谓多数据源,就是JavaEE项目中采用了不同数据库实例中的多个库,或者同一个数据库实例中多个不同的库。一般来说,采用MyCat等分布式数据库中间件是比较好的解决方案,这样可以把数据库读写分离,分库分表,备份等操作。
对于以上的操作不再讲述多数据源操作方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值