2.5 JPA支持

Springboot对JPA(Java Persistence API,java数据持久化,也就是数据库层)的支持很简洁。JPA是指数据库表持久化的一种技术,显著特点是表映射成类。
JPA最著名的实现就是hibernate了。现在我们就来看看Springboot如何使用jpa。
这是一个完整的eclipse项目,点击下载后导入,可直接运行。
首先看看pom.xml,添加了这些:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
其中com.h2database是一个嵌入式数据库h2,spring-boot-starter-data-jpa提供jpa的方法支持。
有三个java文件
1.Customer.java
这是一个数据库映射类,类的所有属性都和表一一对应。表在哪里呢?这个例子使用了一个内嵌的数据库h2,只需要配置pom文件就可以使用了。后面我们会看到。如果熟悉hibernate或者mybatis的就会发现,SpringBoot省去了xml配置文件,而使用了标签。这里@Entity就表示这是一个实体类,对应一个表。@Id表示这个属性是主键:
package com.example.accessingdatajpa;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer{
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
    protected Customer(){
    }
    public Customer(String firstName,String lastName){
        this.firstName=firstName;
        this.lastName=lastName;
    }
    @Override
    public String toString(){
        return String
            .format("Customer[id=%d, firstName='%s', lastName='%s']",id,firstName,lastName);
    }
    public Long getId(){
        return id;
    }
    public String getFirstName(){
        return firstName;
    }
    public String getLastName(){
        return lastName;
    }
}
2.CustomerRepository.java
package com.example.accessingdatajpa;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository{
    ListfindByLastName(String lastName);
    Customer findById(long id);
}
这是一个接口,它需要继承一个接口CrudRepository,这个接口需要将指定泛型,第一个参数是要处理的映射类,第二个参数是这个映射类的主键的类型。
这里定义了两个方法,findByLastName通过lastName查找Customer,结果是一个集合;findById通过主键id查找,结果唯一。
这个接口是用来定义处理Customer表的所有方法的。
3.JPAApplication.java
这是启动类,里面有一个方法:
 
@Bean
public CommandLineRunner demo(CustomerRepository repository){
    return(args)->{
        // 保存一些Customer对象
        repository.save(new Customer("Jack","Bauer"));
        repository.save(new Customer("Chloe","O'Brian"));
        repository.save(new Customer("Kim","Bauer"));
        repository.save(new Customer("David","Palmer"));
        repository.save(new Customer("Michelle","Dessler"));
        // 从数据库取得所有Customer对象
        log.info("Customers found with findAll():");
        log.info("-------------------------------");
        for(Customer customer:repository.findAll()){
            log.info(customer.toString());
        }
        log.info("");
        // 通过id取得一个Customer对象
        Customer customer=repository.findById(1L);
        log.info("Customer found with findById(1L):");
        log.info("--------------------------------");
        log.info(customer.toString());
        log.info("");
        // 通过lastName取得Customer对象
        log.info("Customer found with findByLastName('Bauer'):");
        log.info("--------------------------------------------");
        repository.findByLastName("Bauer").forEach(bauer->{
            log.info(bauer.toString());
        
        })
        ;
        log.info("");
};
含有@bean标签的方法,都会在启动的时候初始化,这个方法展示了Customer的两个获取方法。运行的结果直接显示在控制台:
看到这里,可能已经有人发现:CustomerRepository只是一个接口,并没有实现,实现类在哪里?具体要执行的SQL语句在哪里?
不需要实现类
SpringBoot为我们写了实现!
实现由Spring Data JPA提供,你只需要定义接口,具体SQL语句是自动生成的。在程序运行时,Spring框架会自动为CustomerRepository创建一个实现类,并且此实现类会作为一个Spring Bean被注入到你的服务(Service)层或其他需要依赖注入的组件,供应用程序使用。
只需要命名的时候按照规范。findById和findByLastNamee并不是随便写的,必须在Customer类里存在id和lastName这两个属性,只有通过主键查找才可以返回唯一值,其他的查找必须返回集合,通常就是List
findBy也是规范之一。
关于GRUD操作方法的规范命名,Spring Boot的JPA提供了很完备的命名规范,关于repository的更多细节将在7.8节介绍。
3.修改启动类
在启动类添加方法CommandLineRunner,此方法来演示如何操作数据库。Spring Boot应用在启动时会运行这个方法提供的代码,它合理地使用了JPA仓库来保存和检索Customer实例。现在的启动类DemoApplication 的代码如下所示:
package com;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.accessingdatajpa.Customer;
import com.example.accessingdatajpa.CustomerRepository;
@SpringBootApplication@RestControllerpublic 
class DemoApplication{
    private static final Logger log=LoggerFactory.getLogger(DemoApplication.class);
    public static void main(String[]args){
        SpringApplication.run(DemoApplication.class,args);
    }
    @GetMapping("/hello")
    public String hello(@RequestParam(value="name",defaultValue="World")String name){
        return String.format("Hello %s!",name);
    }
    @Beanpublic 
    CommandLineRunner demo(CustomerRepository repository){
        return(args)-{
            // 保存一些Customer对象
            repository.save(new Customer("Jack","Bauer"));
            repository.save(new Customer("Chloe","O'Brian"));
            repository.save(new Customer("Kim","Bauer"));
            repository.save(new Customer("David","Palmer"));
            repository.save(new Customer("Michelle","Dessler"));
            // 从数据库取得所有Customer对象
            log.info("Customers found with findAll():");
            log.info("-------------------------------");
            for(Customer customer:repository.findAll()){
                log.info(customer.toString());
            }
            log.info("");
            // 通过id取得一个Customer对象
            Customer customer=repository.findById(1L).get();
            log.info("Customer found with findById(1L):");
            log.info("--------------------------------");
            log.info(customer.toString());
            log.info("");
            // 通过lastName取得Customer对象
            log.info("Customer found with findByLastName('Bauer'):");
            log.info("---------------------------------");
            repository.findByLastName("Bauer").forEach(bauer -{
                log.info(bauer.toString());
            
            });
            log.info("");
        };
    }
}
代码7-13在启动类添加测试方法
含有@bean标签的方法,都会在启动的时候初始化,这个方法展示了Customer的两个获取方法。重新启动Spring Boot,运行的结果直接显示在控制台,如图所示: