JPA
项目地址:https://github.com/zhaobao1830/misszb
简介
JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范。主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。
JPA的优势:标准化、容器级特性的支持、简单方便、查询能力、高级特性。
与Hibernate的关系:
JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。
SpringBoot整合
依赖安装
访问的是mysql数据库
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.32</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
配置文件
spring:
datasource:
url: 数据库路径
username:
password:
jpa:
properties:
hibernate:
# 查询的时候在控制台显示jpa生成的sql
show_sql: true
# 格式化控制台显示的sql
format_sql: true
使用
以获取banner信息为例,展示如何使用JPA
一、定义实体类
Banner实体类
package com.zb.misszb.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
@Entity
@Getter
@Setter
@Table(name = "banner")
public class Banner extends BaseEntity {
@Id
private Long id;
private String name;
private String description;
private String title;
private String img;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "bannerId")
private List<BannerItem> items;
}
items字段,保存的是BannerItem表的信息
导航关系是一对多,通过@OneToMany(fetch = FetchType.LAZY)注解来定义
fetch = FetchType.LAZY:表示延迟加载
@JoinColumn(name = "bannerId")注解设置逻辑外键为bannerId
BannerItem实体类
package com.zb.misszb.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "banner_item")
public class BannerItem extends BaseEntity {
@Id
private Long id;
private String img;
private String keyword;
private short type;
private String name;
private Long bannerId;
}
二、定义Repository
package com.zb.misszb.repository;
import com.zb.misszb.model.Banner;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BannerRepository extends JpaRepository<Banner, Long> {
Banner findOneById(Long id);
Banner findOneByName(String name);
}
需要继承JpaRepository接口,泛型传入的值第一个是查询的实体类,第二个是实体类主键的类型
在这里定义操作数据库的方法
三、定义Service和ServiceImpl
BannerService
package com.zb.misszb.service;
import com.zb.misszb.model.Banner;
public interface BannerService {
Banner getBannerByName(String name);
}
BannerServiceImpl
package com.zb.misszb.service.impl;
import com.zb.misszb.model.Banner;
import com.zb.misszb.repository.BannerRepository;
import com.zb.misszb.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BannerServiceImpl implements BannerService {
@Autowired
private BannerRepository bannerRepository;
@Override
public Banner getBannerByName(String name) {
return bannerRepository.findOneByName(name);
}
}
第四、定义Controller
BannerController
package com.zb.misszb.api.v1;
import com.zb.misszb.exception.http.NotFoundException;
import com.zb.misszb.model.Banner;
import com.zb.misszb.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
@RestController
@RequestMapping(value = "/banner")
@Validated
public class BannerController {
@Autowired
private BannerService bannerService;
@RequestMapping(value = "/name/{name}", method = RequestMethod.GET)
public Banner getBannerName(@PathVariable @NotBlank String name) {
Banner banner = bannerService.getBannerByName(name);
if (banner == null) {
throw new NotFoundException(30005);
}
return banner;
}
}
说明
一、一个功能的实现是:Controller => Service => Repository(=>表示调用)
二、@OneToMany(fetch = FetchType.LAZY)
FetchType.LAZY表示延迟加载
我们在查询的时候没有写关于关联表BannerItem的代码,为什么还是能查询出来呢?
这是因为SpringBoot里自带的序列化插件jackson,在序列化的时候,会将实体类里的所有字段进行序列化(没有被@JsonIgnore注解的字段),如果当前字段 关联的其他表,就是去查询关联表
换个说法就是,如果没有序列化关联的字段,那就不会切查询关联表,因为设置了FetchType.LAZY延迟加载
命名方法查询规则
在SpringBoot中由于有JPA,对于简单的查询,是无需写SQL的。JPA提供了命名查询的方式,通过简单的方法命名即可由JPA来解析生成SQL。
1、方法名必须以find、read、get等开头,后面紧跟要查询的实体类名称,例如findByUser或者readByUser
2、可以在实体类名称后面添加一个或多个属性名,以By关键字分割,例如findByUserAndTitle
3、对于属性名,可以使用“属性名”、“属性名关键字属性名”、“属性名_关键字_属性名”等格式进行组合,例如“findByUserName”、“findByUserNameLike”、“findByUserName_Like”
4、可以在属性名后面添加一些关键字,例如“IgnoreCase”、“OrderBy”、“First”、“Top”、“Distinct”等,用于实现不区分大小写、排序、分页、去重等功能
5、如果方法需要传入参数,则在方法名中使用“参数占位符”来表示,例如"findByUserNameAndAgeGreaterThan(String userName, int age)",这里面的“userName”和“age”都是参数占位符
6、参数占位符可以使用“?”或者":参数名"的形式来表示,例如"findByUsernameAndAgeGreaterThan(String userName, int age)"可以写成"findByUserNameAndAgeGreaterThan(String userName, @Param(“age”) int age)"
7、如果方法需要使用原生的SQL查询,则可以使用“@Query”注解,并在其中定义SQL语句,例如“@Query(“select * from user where user_name = ?1”)”
关联方式
一对一
查询学生和学生信息
第一种方式:使用外键
这里的外键是逻辑外键,通过student里的information_id与information表里的id进行管理
student.java
package com.zb.misszb.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.util.List;
@Entity
@Getter
@Setter
@Table(name = "student")
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
// 一对一
// 通过student表里定义的information_id外键和information表里的id关联进行查询
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "informationId", referencedColumnName = "id", insertable = false, updatable = false)
private Information information;
}
student表
备注
student类中的@JoinColumn添加insertable = false, updatable = false属性,表明是只读的,不然项目启动会报错
package com.zb.misszb.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "information")
public class Information {
@Id
private Long id;
private String name;
private Long studentId;
}
第二种方式:使用共享主键
就是把information表中的studentId作为student的外键
student.java
package com.zb.misszb.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.util.List;
@Entity
@Getter
@Setter
@Table(name = "student")
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
private Long informationId;
@OneToOne(mappedBy = "student", cascade = CascadeType.ALL)
@JsonIgnoreProperties("student")
private Information information;
}
Information.java
package com.zb.misszb.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "information")
public class Information {
private Long id;
private String name;
@Id
private Long studentId;
@OneToOne
@MapsId
@JoinColumn(name = "studentId")
@JsonIgnoreProperties("information")
private Student student;
}
备注
@JsonIgnoreProperties注解可以让查询出的数据不会出现无限嵌套
备注
这种方式我还不理解,先记录下来
第三种方式:建立关联表
数据库新建学生表、学生信息表和关联表三张表
新建Student和Information实体类
Student.java
package com.zb.misszb.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "student")
public class Student {
@Id
@GeneratedValue
private Long id;
private String studentName;
@OneToOne(cascade = CascadeType.ALL)
@JoinTable(name = "student_information",
joinColumns = @JoinColumn(name="student_id"),
inverseJoinColumns = @JoinColumn(name="information_id")
)
private Information information;
}
Information.java
package com.zb.misszb.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "information")
public class Information {
@Id
@GeneratedValue
private Long id;
private String informationName;
}
关联表是student_information
里面的字段是student_id(对应的是student表里的id)和information_id(对应的是information表里的id)
controller:
@RequestMapping(value = "/getStudent", method = RequestMethod.GET)
public Student getStudent(@RequestParam Long id) {
Optional<Student> Student = StudentRepository.findById(id);
return Student.get();
}
多对多
项目参考:https://github.com/zhaobao1830/misszb/项目中model/Theme
theme和spu是多对多关系
数据库新建theme表、spu表、them_spu表,theme和spu有实体类
theme.java
package com.zb.misszb.model;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Where;
import javax.persistence.*;
import java.util.List;
@Entity
@Getter
@Setter
@Where(clause = "delete_time is null")
public class Theme extends BaseEntity{
@Id
private Long id;
private String title;
private String description;
private String name;
private String extend;
private String entranceImg;
private String internalTopImg;
private Boolean online;
private String titleImg;
private String tplName;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name="theme_spu", joinColumns = @JoinColumn(name="theme_id")
, inverseJoinColumns = @JoinColumn(name="spu_id"))
private List<Spu> spuList;
spu.java
package com.zb.misszb.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
@Entity
@Getter
@Setter
public class Spu extends BaseEntity {
@Id
private Long id;
private String title;
private String subtitle;
private Long categoryId;
private Long rootCategoryId;
private Boolean online;
private String price;
private Long sketchSpecId;
private Long defaultSkuId;
private String img;
private String discountPrice;
private String description;
private String tags;
private Boolean isTest;
private String forThemeImg;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "spuId")
private List<Sku> skuList;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "spuId")
private List<SpuImg> spuImgList;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "spuId")
private List<SpuDetailImg> spuDetailImgList;
}
theme_spu表:包含them_id和spu_id