为什么需要JPA?
- jpa是用来映射领域类和物理表之间的关系的,有了jpa才能够真正意义上的将物理表中的内容提取到领域类中,以及将领域类中的内容映射到物理表中
- 因此,我们需要安装jpa依赖,同样在pom.xml文件中直接添加就可以了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
JPA在领域类中的体现
- 想要将一个领域类映射到物理表中,需要首先加上@Entity注解,添加到spring上下文管理之中
- jpa需要领域类有一个有参的构造方法,所以需要在领域类上加上@RequiredArgsConstructor注解
- jpa进行映射时会通过类名映射到对应的表,比如User类就对应user表,但是我们的表名叫做users,所以需要加上@Table(name = “users”)注解来指定表名
- 同样字段也会映射到对应名字的字段,比如username就映射到username,但我们有些字段是驼峰式命名的,比如joinedDate,这时就需要加上@Column(name = “joined_date”)注解来指定字段名
- jpa需要指定一个唯一id,在主键字段上要加上@Id注解,如果id是按照某种方式生成的,还需要加上@GeneratedValue注解
- 下面给出User类的一个样例
package kmhc.domain.user;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.OneToMany;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
@Entity
@Table(name = "users")
public class User implements UserDetails {
/**
*
*/
// Serializable
private static final long serialVersionUID = 1L;
// username primary key
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// username
private String username;
// password
private String password;
// is active default is true
private boolean enabled = true;
// first name
@Column(name = "first_name")
private String firstName;
// last name
@Column(name = "last_name")
private String lastName;
// gender default is 0
private short gender;
// phone
private String phone;
// email
private String email;
// icon
private String icon;
// birthday
private Date birthday;
// joined date
@Column(name = "joined_date")
private Date joinedDate;
@PrePersist
void joinedDate() {
this.joinedDate = new Date();
}
// authorities
@OneToMany(targetEntity = Authority.class, mappedBy = "user", cascade = { CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.EAGER)
private List<Authority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
List<GrantedAuthority> auths = new LinkedList<>();
for (Authority authority : this.authorities) {
auths.add(new SimpleGrantedAuthority(authority.getAuthority()));
}
return auths;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return this.enabled;
}
}
JPA的一对多、多对一、多对多
- 以上三种是jpa中比较常用的表之间关系的映射
- 一对多和多对一不一定是成对出现的,有的时候只需要其中一方就可以了
- 下面开始讲解这些关系的核心要素
@OneToMany
- 一对多,经常出现在User类中,我们需要得知这个user都具备哪些权限
- 一般作用在一个List上
- 有几个关键属性
- targetEntity(指定多的那一方对应的类,可以不写)
- mappedBy(指定本类,在多的那一方的类中,属性名是什么)
- cascade(指定级联关系,在一对多中一般只要MERGE和REMOVE两种就可以)
- fetch(指定懒加载还是快加载,默认懒加载)
@ManyToOne
- 多对一,一般来说在多的那一方必然存在,经常用在获取某个文章的作者
- 有几个关键属性
- targetEntity(指定一的那一方对应的类,可以不写)
- optional(一的那一方是否是可选的,一般是false,即必须要有)
- @JoinColumn
- 这个注解一般是和@ManyToOne同时作用在一个属性上的
- @JoinColumn(name = “user_id”)代表这个属性在真实物理表中的字段名叫做user_id,不加这个注解默认会以属性名加主键名的方式代表字段名
@ManyToMany
- 多对多,当两个领域类存在多对多关系的时候就要有这个注解,由其中一方维护即可
- 一般作用在List上
- 有几个关键属性
- targetEntity(指定另一方对应的类,可以不写)
- @JoinTable
- 这个注解一般是和@ManyToMany同时作用在一个属性上的
- @JoinTable(name = “group_members”, joinColumns = @JoinColumn(name = “group_id”), inverseJoinColumns = @JoinColumn(name = “user_id”))
- 可以看到,此注解不但制定了多对多对应的表名,还用了@JoinColumn注解
- joinColumns属性代表本类所代表的外键
- inverseJoinColumns属性代表另一个类所代表的外键
Validation
- 现在用了jpa进行映射,但是无法保证这个映射一定能够满足数据库用户自定义完整性的要求,因此我们需要安装java validation依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 同样Validation也是用了注解的方式对属性进行验证,不满足要求的后端会直接返回一个400状态码
- 如果不在类上加上@Validate注解,是不会进行自动验证的
- 有关jpa和validation的结合请看下面代码
JPA和Validation的完整代码
package kmhc.domain.user;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.persistence.OneToMany;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
@Entity
@Table(name = "users", uniqueConstraints = { @UniqueConstraint(columnNames = { "username" }) })
public class User implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 1L;
// id primary key
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// username unique
@NotNull
private String username;
// password
@Size(min = 6, message = "password must be at least 6 characters!")
private String password;
// is active default is true
private boolean enabled = true;
// first name
@Column(name = "first_name")
private String firstName;
// last name
@Column(name = "last_name")
private String lastName;
// gender default is 0
private short gender = 0;
// phone
private String phone;
// email
@Email
private String email;
// icon
private String icon;
// birthday default is current
private Date birthday = new Date();
// joined date default is current
@Column(name = "joined_date")
private Date joinedDate = new Date();
// authorities
@OneToMany(targetEntity = Authority.class, mappedBy = "user", cascade = { CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.EAGER)
private List<Authority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
List<GrantedAuthority> auths = new LinkedList<>();
for (Authority authority : this.authorities) {
auths.add(new SimpleGrantedAuthority(authority.getAuthority()));
}
return auths;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return this.enabled;
}
}
package kmhc.domain.user;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
@Entity
@Table(name = "authorities")
public class Authority implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1271128647094978200L;
// id primary key
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// authority name
@NotNull
private String authority;
// user id
@ManyToOne(targetEntity = User.class, optional = false)
@JoinColumn(name = "user_id")
private User user;
}
package kmhc.domain.user;
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.persistence.JoinColumn;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
@Entity
@Table(name = "user_groups")
public class Group implements Serializable {
/**
*
*/
private static final long serialVersionUID = -63681634856481619L;
// id
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// group name
@NotNull
@Column(name = "group_name")
private String groupName;
// group authorities
@OneToMany(targetEntity = GroupAuthority.class, mappedBy = "group", cascade = { CascadeType.MERGE, CascadeType.REMOVE })
private List<GroupAuthority> authorities;
// group users
@ManyToMany(targetEntity = User.class)
@JoinTable(name = "group_members", joinColumns = @JoinColumn(name = "group_id"), inverseJoinColumns = @JoinColumn(name = "user_id"))
private List<User> users;
}
package kmhc.domain.user;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
@Entity
@Table(name = "group_authorities")
public class GroupAuthority implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5792431421556924268L;
// id
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// authority name
@NotNull
private String authority;
// group name
@ManyToOne(targetEntity = Group.class, optional = false)
@JoinColumn(name = "group_id")
private Group group;
}
至此,领域类中的JPA和Validation已完成,但是仍然没有数据库的连接动作和结果集的提取。下一节我们会讲解repository以及回到spring security上来