SpringBoot整合JPA
1、什么是JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。——百度百科
通俗的来说,就是我们只需要在项目中继承JPA的接口,在配置文件中选择实现JPA标准的框架(Hibernate、TopLink、JDO),JPA就可以帮我们完成大部分的增删改查的功能,省去了编写DAO层的步骤。
2、JPA基于注解的使用
注解 | 功能 |
@Entity | 声明类为实体或表。 |
@Table | 声明表名。 |
@Basic | 指定非约束明确的各个字段。 |
@Embedded | 指定类或它的值是一个可嵌入的类的实例的实体的属性。 |
@Id | 指定的类的属性,用于识别(一个表中的主键)。 |
@GeneratedValue | 指定如何标识属性可以被初始化,例如自动、手动、或从序列表中获得的值。 |
@Transient | 指定的属性,它是不持久的,即:该值永远不会存储在数据库中。 |
@SequenceGenerator | 指定持久属性栏属性。 |
@TableGenerator | 指定在@GeneratedValue注解中指定的属性的值。它创建了一个序列。 |
@AccessType | 指定在@GeneratedValue批注指定属性的值发生器。它创造了的值生成的表。 |
@JoinColumn | 这种类型的注释用于设置访问类型。如果设置@AccessType(FIELD),则可以直接访问变量并且不需要getter和setter,但必须为public。如果设置@AccessType(PROPERTY),通过getter和setter方法访问Entity的变量。 |
@UniqueConstraint | 指定一个实体组织或实体的集合。这是用在多对一和一对多关联。 |
@ColumnResult | 指定的字段和用于主要或辅助表的唯一约束。 |
@ManyToMany | 参考使用select子句的SQL查询中的列名。 |
@ManyToOne | 定义了连接表之间的多对多一对多的关系。 |
@OneToMany | 定义了连接表之间的多对一的关系。 |
@OneToOne | 定义了连接表之间有一个一对一的关系。 |
@NamedQueries | 指定命名查询的列表。 |
@NamedQuery | 指定使用静态名称的查询。 |
3、项目实践
3.1、创建spring boot项目
在左侧点击SQL选项,选中JPA与MYSQL,再引入test的模块创建完成的目录结构
3.2、pom文件中的相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tianqicode</groupId>
<artifactId>spring-boot-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-jpa</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- JPA启动器依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- web启动器依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 测试相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.3、配置文件
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/how2java?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
jpa.hibernate.ddl-auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下:
·create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
·create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
·update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
·validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
3.4、创建实体类
package com.tianqicode.testjpa.bean;
import javax.persistence.*;
@Entity
@Table(name = "category_")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Category{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
@Entity 注解表示这是个实体类
@Table(name = “category_”) 表示这个类对应的表名是 category_
@Id 表明主键
@GeneratedValue(strategy = GenerationType.IDENTITY) 表明自增长方式
@Column(name = “id”) 表明对应的数据库字段名
3.5、创建CategoryDAO
package com.tianqicode.testjpa.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.tianqicode.testjpa.bean.Category;
public interface CategoryDAO extends JpaRepository<Category, Integer> {
}
3.6、创建CategoryController
package com.tianqicode.testjpa.controller;
import com.tianqicode.testjpa.bean.Category;
import com.tianqicode.testjpa.dao.CategoryDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
public class CategoryController {
@Autowired
CategoryDAO categoryDAO;
@RequestMapping(value = "test")
public List<Category> categoryList() {
List<Category> cs = categoryDAO.findAll();
return cs;
}
}
3.7、编写测试类
package com.tianqicode.testjpa.controller;
import com.tianqicode.testjpa.SpringBootJpaApplication;
import com.tianqicode.testjpa.bean.Category;
import com.tianqicode.testjpa.service.CategoryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)//使用spring环境运行,否则无法注入对象
@SpringBootTest(classes = SpringBootJpaApplication.class)
public class test {
@Autowired
CategoryService categoryService;
@Test
public void testJPA() {
List<Category> cs = categoryService.findAll();
for (Category category : cs) {
System.out.println(category);
}
}
}
3.7、运行
控制台成功遍历输出了数组同时也发现了日志中hibernate输出的日志:
4、JPA的条件查询
4.1、认识JPA的条件查询
JPA中其实准备了一部分的条件查询,例如findById。但是如果需要根据数据表中的外键进行查询,则需要我们自己为接口添加方法。
JPA在这里遵循Convention over configuration(约定大约配置)的原则,遵循spring 以及JPQL定义的方法命名。Spring提供了一套可以通过命名规则进行查询构建的机制。这套机制会把方法名首先过滤一些关键字,比如 find…By, read…By, query…By, count…By 和 get…By 。系统会根据关键字将命名解析成2个子语句,第一个 By 是区分这两个子语句的关键词。这个 By 之前的子语句是查询子语句(指明返回要查询的对象),后面的部分是条件子语句。如果直接就是 findBy… 返回的就是定义Respository时指定的领域对象集合,同时JPQL中也定义了丰富的关键字:and、or、Between等等
4.2、项目实践
数据库category
数据库Product_
这里Product表的外键是cid,这里没有在MySQL中设置外键,而是在代码中进行约束
Product实体类
package com.tianqicode.testjpa.bean;
import javax.persistence.*;
@Entity
@Table(name = "product_")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "price")
private float price;
@ManyToOne//多对一约束
@JoinColumn(name = "cid")//声明外键所在的列
private Category category;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", category=" + category +
'}';
}
}
DAO接口
package com.tianqicode.testjpa.dao;
import com.tianqicode.testjpa.bean.Category;
import com.tianqicode.testjpa.bean.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProductDAO extends JpaRepository<Product, Integer> {
List<Product> findByCategory(Category category);
}
方法的命名要遵循驼峰命名的规则,且所有关键词区分大小写,在方法中传入需要的对象参数
Controller类
package com.tianqicode.testjpa.controller;
import com.tianqicode.testjpa.bean.Category;
import com.tianqicode.testjpa.bean.Product;
import com.tianqicode.testjpa.dao.ProductDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import java.util.List;
@Controller
public class ProductController {
@Autowired
ProductDAO productDAO;
public List<Product> findAll() {
List<Product> ps = productDAO.findAll();
return ps;
}
public List<Product> findByCategory(Category category) {
List<Product> ps = productDAO.findByCategory(category);
return ps;
}
}
测试类
@Test
public void testProductFindByCategory() {
Category category = new Category();
category.setId(4);
List<Product> ps = productController.findByCategory(category);
for (Product product : ps) {
System.out.println(product);
}
运行
条件查询成功。