Spring Boot框架(四)--Spring Boot中使用JPA

前面介绍了springboot框架的使用,但是一直都没有涉及到数据库的操作问题,数据库操作当然也是我们在开发中无法回避的问题,看一下Spring Boot提供了哪些方式来解决数据库的操作问题。


什么是JPA


JPA的全称叫做Java Persistence API,JPA是一个基于O/R映射的标准规范,在这个规范中,JPA只定义标准规则,不提供实现,使用者则需要按照规范中定义的方式来使用。目前JPA的主要实现有Hibernate、EclipseLink、OpenJPA等,事实上,由于Hibernate在数据访问解决技术领域的绝对霸主地位,JPA的标准基本是由Hibernate来主导的。另外,Spring框架为我们提供了Spring Data JPA这样一个东西,可以减少我们使用JPA时的代码量。

使用流程


创建工程并添加相关依赖


在Spring Boot中使用JPA,我们在创建工程的时候需要选择JPA依赖和添加MySql驱动,如下:

 配置基本属性


接下来需要我们在application.properties中配置数据源和jpa的基本的相关属性,如下:

#表示驱动的名称,这个和具体的数据库驱动有关,这里使用了MySql数据库,所以驱动名为com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#表示数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/jpatest
#数据库连接的用户名和密码
spring.datasource.username=root
spring.datasource.password=123456

#配置了实体类维护数据库表结构的具体行为,update表示当实体类的属性发生变化时,
#表结构跟着更新,这里我们也可以取值create,
#这个create表示启动的时候删除上一次生成的表,
#并根据实体类重新生成表,这个时候之前表中的数据就会被清空;
#还可以取值create-drop,这个表示启动时根据实体类生成表,但是当sessionFactory关闭的时候表会被删除;
#validate表示启动时验证实体类和数据表是否一致;none表示啥都不做。
spring.jpa.hibernate.ddl-auto=update
#表示hibernate在操作的时候在控制台打印真实的sql语句
spring.jpa.show-sql=true
#表示格式化输出的json字符串
spring.jackson.serialization.indent_output=true

定义映射实体类


接下来,定义相应的实体类,在Project启动时,系统会根据实体类创建相应的数据表,实体类如下:

package springboot.jpa.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

/**
 * @Author:dyz Date:18:08 2019/7/18
 */
@Entity
@NamedQuery(name = "Student.withNameAndAddressNamedQuery",
        query = "select s from Student s where s.name=?1 and s.address=?2")
public class Student {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private Integer age;
    private String address;

    public Student() {
    }

    public Student(Long id, String name, Integer age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

实体类上使用了@Entity注解,这个表示这是一个和数据库表映射的实体类,在属性id上添加了@Id注解,表示该字段是一个id,@GeneratedValue注解则表示该字段自增。@NamedQuery注解表示一个NamedQuery查询,这里一个名称代表一个查询语句,我们一会可以在控制器中直接调用@NamedQuery中的withNameAndAddressNamedQuery方法,该方法代表的查询语句是select p from Person p where p.name=?1 and p.address=?2。
 

定义数据访问接口


OK,做好上面几个步骤之后,接下来我们就可以定义数据访问接口了,我们的数据访问接口需要继承JpaRepository类,在数据访问接口中一共定义了四个方法,如下:

package springboot.jpa.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import springboot.jpa.entity.Student;

import java.util.List;

/**
 * @Author:dyz Date:18:22 2019/7/18
 */
public interface StudentRepository extends JpaRepository<Student, Long> {
    List<Student> findByAddress(String name);

    Student findByNameAndAddress(String name, String address);

    @Query("select s from Student s where s.name=:name and s.address=:address")
    Student withNameAndAddressQuery(@Param("name") String name, @Param("address") String address);

    Student withNameAndAddressNamedQuery(String name, String address);

}

需要注意的一点是,当我们继承JpaRepository接口后,我们就自动具备了如下数据访问方法:

List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAll(Iterable<ID> var1);
<S extends T> List<S> save(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);

2.我们可以在接口中定义查询方法,可以按照属性名来查询,但是方法的命名方式是固定的,比如第一个方法和第二个方法,第一个方法表示根据一个属性查询,第二个方法表示根据多个属性查询,findBy、And等可以算作是这里的查询关键字了,如果写作其他名称则系统不能识别,类似的关键字还有Like、Or、Is、Equals、Between等,而这里的findBy关键字又可以被find、read、readBy、query、queryBy、get、getBy等来代替。

3.在查询的过程中我们也可以限制查询结果,这里使用的关键字是top、first等,比如查询前10条数据我们可以写作:
 

List<Student> findFirst10ByName(String name);

4.使用NamedQuery来查询,就是我们直接在实体类上使用@NamedQuery注解来定义查询方法和方法名,一个名称对应一个查询语句,具体可以参考我们上文的实体类
5.我们也可以向第三个方法那样添加@Query注解,当我调用这个方法的时候使用这个注解中的sql语句进行查询,方法的参数则是注解中的占位符的值。


编写测试Controller


数据访问接口都有了,接下来写一个简单的Controller,如下:

package springboot.jpa.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springboot.jpa.dao.StudentRepository;
import springboot.jpa.entity.Student;

import java.util.List;

/**
 * @Author:dyz Date:18:23 2019/7/18
 */
@RestController
public class DataController {
    @Autowired
    StudentRepository studentRepository;

    @RequestMapping("/save")
    public Student save(String name, String address, Integer age) {
        Student student = studentRepository.save(new Student(null, name, age, address));
        return student;
    }

    @RequestMapping("/q1")
    public List<Student> q1(String address) {
        List<Student> people = studentRepository.findByAddress(address);
        return people;
    }

    @RequestMapping("/q2")
    public Student q2(String name, String address) {
        Student people = studentRepository.findByNameAndAddress(name, address);
        return people;
    }

    @RequestMapping("/q3")
    public Student q3(String name, String address) {
        Student student = studentRepository.withNameAndAddressQuery(name, address);
        return student;
    }

    @RequestMapping("/q4")
    public Student q4(String name, String address) {
        Student student = studentRepository.withNameAndAddressNamedQuery(name, address);
        return student;
    }
    @RequestMapping("/sort")
    public List<Student> sort() {
        List<Student> people = studentRepository.findAll(new Sort(Sort.Direction.ASC, "age"));
        return people;
    }
    @RequestMapping("/page")
    public Page<Student> page(int page, int size){
        Page<Student> all = studentRepository.findAll(new PageRequest(page, size));
        return all;
    }
    @RequestMapping("/all")
    public List<Student> all(){
        return studentRepository.findAll();
    }
}

其中上面的第36行代码表示根据age对查询结果进行排序然后显示出来,第40行的方法表示一个分页查询,第一个参数表示页数,从0开始计,第二个参数表示每页的数据量。

最后我们启动项目,会发现数据库自动生成两个表,如下:

然后我们可以测试一下第一个接口,如下:

 

 我们可以看到后台已经打印出SQL语句,并且数据库也已经保存了新的数据:

 

 剩下的分别测试这几个接口就可以了,我这里就不再展示测试页面了。

原创文章 61 获赞 84 访问量 1万+

猜你喜欢

转载自blog.csdn.net/MICHAELKING1/article/details/96437663