springboot-jpa
最近在研究Spring的jpa的动态查询,使用比较方便,但是着实也踩了不少的坑,简单的总结一下。
- JPA简介
Java Persistence API(JPA),是一组用于将数据存入数据库的类和方法的集合,为POJO提供持久化标准规范,通过注解格式声明,用于定义实体类映射到关系数据表的规则。
本人描述不够到位,具体可以参考官方详细说明
jpa动态查询案例参考说明 –注意版本号,代码书写是有变化的 使用idea+maven
pom文件添加依赖如下:
<!--jpa-spec动态查询-->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>2.2.1</version>
</dependency>
两个关联表(User和Address)配置:
引用lombok依赖便可以利用注解省略setter和getter方法,下面代码是两个关联表多对多,一对多或多对一可以参考上面的案例参考,里面会有详细的介绍,本人主要是在多对多模式入坑,所以主要分析这部分:
- 首先需要在两个实体类里面声明彼此,多对多模式就彼此声明了彼此的数组(如果是一对多或多对一,只声明多的一方为数组即可),主要不同的就是两个类的注解不同,具体请往下看:
@Setter
@Getter
@Entity
@Table(name = "user")
public class User implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id")
private Integer userId;
@Column(name = "name")
private String name;
@Column(name = "sex")
private String sex;
@Column(name = "birthday")
private Date birthday;
@Column(name = "height")
private String height;
@ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
@JoinTable(name = "user_addresses",
joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "address_id",referencedColumnName = "address_id")})
private List<Address> addresses;
}
@Entity
@Table(name = "address")
@Setter
@Getter
public class Address implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "address_id")
private Integer addressId;
@Column(name = "address_name")
private String addressName;
@ManyToMany(fetch = FetchType.LAZY,mappedBy = "addresses")
@JsonIgnore
private List<User> users;
}
两个实体类都需在声明的数组上添加@ManyToMany的注解,该注解可添加属性,如cascade是关于级联操作的属性设置;fetch是关于加载方式的属性设置,LAZY表示懒加载,EAGER表示立即加载;mappedBy表示当前类放弃字段维护权,即放弃控制关联关系,不能使用@JoinColumn等表示关联的注解;@JsonIgnore注解是多个表关联查询必不可少的,防止关联类进行多次的重复查询;未设置 mappedBy的一方控制关联关系,需通过添加@JoinTable注解来确定关联表及关联字段,该注解必填属性,name表示关联表的名称,joinColumns是主操作表的中间表列,即维护端字段,而inverseJoinColumns是副操作表的中间表列,referencedColumnName 标注的是所关联表中的字段名,若不指定则使用的所关联表的主键字段名作为外键。
实体类的注解介绍完毕,现在就开始进行数据的查询,首先继承JpaRepository,里面有封装好代码,可以通过方法名进行数据的操纵。
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {}
- 前面声明引入的jpa-spec依赖到此开始正式派上用场,Specification中包含很多语法,模糊查询、区间查询等,本次只是使用的准确查找,目的是讲解使用的坑,具体可自行研究。服务层代码如下:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> manyToMany(Integer id){
// 3.0版本的写法
// Specification<Address> specification = Specifications.<Address> and()
// .eq("addresses.addressId",id)
// .build();
//当前版本的写法
Specification<User> specification = new Specifications<User>()
// .eq("userId",id)
.eq("addresses.addressId",id)
.build();
return userRepository.findAll(specification);
}
}
代码的维护端是User,即本人理解是从User发起的多对多的查询,如果查询的是维护端的属性,则直接填写相应的实体类属性名称(注意:不是表字段),如果查询的条件是关联类的属性,需要进行限定,即mappedBy中定义的关联类名称.属性名称,如“addresses.addressId”。
最后就是进行最后的结果验收了,入口代码如下:
@GetMapping("/ManyToMany")
public List<User> manyToMany(Integer id){
return userService.manyToMany(id);
}
查询的结果如下:
本人在使用过程中遇到的坑如下:
名称不符
多次循环查询问题(@JsonIgnore)
说明:本人通过亲身体验,按照自己的习惯去理解和使用动态查询,如有不对的地方请多多指教!