1、关于Spring Data JPA
JPA(Java Persistence API)是Sun官方提出的Java持久化规范,为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。
它的出现是为了简化现有的持久化开发工作和整合ORM技术,结束各个ORM框架各自为营的局面。
Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,是基于Hibernate之上构建的JPA使用解决方案,用极简的代码实现了对数据库的访问和操作,包括了增、删、改、查等在内的常用功能。
二、简单使用
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2、添加数据源
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
3、创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "t_dict")
public class Dict {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name="dict_key")
private String key;
@Column(name="dict_value")
private String value;
private String tableName;
private String columnName;
}
4、编写controller进行测试
@SpringBootApplication
@RestController
public class DataJpaApplication {
@Autowired
DictRepository dictRepository;
public static void main(String[] args) {
SpringApplication.run(DataJpaApplication.class, args);
}
@RequestMapping("/add")
public Dict add(){
Dict dict = new Dict(null, "1", "普通软件","software","type");
return dictRepository.save(dict);
}
@RequestMapping("/")
public Iterable list(){
return dictRepository.findAll();
}
}
三、使用注解定义实体
1、@Entity
在类上使用,表明这个类是实体类,当类名与表面不一致时,使用@Table(name=””)注解,在name属性中定义表名。
2、@Id
在属性上使用,表明这是主键。
3、@GeneratedValue(strategy=GenerationType.AUTO)
主键生成策略,可以是:
- TABLE 使用一个特定的数据库表来保存主键
- SEQUENCE 使用序列
- IDENTITY 数据库自增
- AUTO 根据数据库选择最合适的
3、@Column
在属性上使用,可以使用name定义对应字段名称
4、@Transient
表示该属性并非一个到数据库表的字段的映射
5、@OneToOne/@OneToMany/@ManyToOne/@ManyToMany
N对N注解,可以定义:
- cascade:级联策略,默认无
- fetch:是否延迟加载,默认直接EAGER
- optional:定义关联实体是否可以存在null值,默认true
- mappedBy:双向关联实体时,标注在不保存关系的实体
- targetEntity: 表示关联的实体类型,默认为当前标注的实体类
CascadeType包含的类别
- 级联:给当前设置的实体操作另一个实体的权限
- CascadeType.ALL 级联所有操作
- CascadeType.PERSIST 级联持久化(保存)操作
- CascadeType.MERGE 级联更新(合并)操作
- CascadeType.REMOVE 级联删除操作
- CascadeType.REFRESH 级联刷新操作
- CascadeType.DETACH 级联分离操作,如果你要删除一个实体,但是它有外键无法删除,这个级联权限会撤销所有相关的外键关联。
6、@JoinColumn
- name:本表的外键
- referencedColumnName:外键所关联表的字段名
7、@JoinTable
- name:属性为连接两个表的表名称。若不指定,则使用默认的表名称,格式如下: “表名1” + “_” + “表名2”
- joinColumn:属性表示,在保存关系的表中,所保存关联关系的外键的字段,并配合@JoinColumn标记使用
- inverseJoinColumn:属性与joinColumn类似,它保存的是保存关系的另外一个外键字段;
- catalog和schema:属性表示实体指定点目录名称或数据库名称;
- uniqueConstraints:属性表示该实体所关联的唯一约束条件,一个实体可以有多个唯一约束条件,默认没有约束;
四、N对N关系映射的使用
1、注意
- 一般在在维护关系的一方给cascade进行权限配置,同时在另一方使用mappedBy
- 直接使用外键关联的使用@JoinColumn注解,如果名称符合规范则可以省略
- 在使用中间表进行关联的使用@JoinTable注解
2、一对一
这里定义两张表,用户表和用户信息表,一个用户表对应一个用户信息:
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
private String password;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "userInfoId", referencedColumnName = "id")
private UserInfo userInfo;
}
@Data
@Entity
@Table(name = "t_user_info")
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private Integer sex;
private Integer age;
// @OneToOne(cascade = {},mappedBy = "userInfo")
// private User user;
}
测试:
@Test
void testOnetoOne() {
UserInfo userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setAge(18);
User user = new User();
user.setUsername("zhangsan");
user.setPassword("123456");
user.setUserInfo(userInfo);
userRepository.save(user);
List<User> userList = userRepository.findByUsername("zhangsan");
Assert.notNull(userList, "获取记录失败");
System.out.println(userList.get(0));
}
3、一对多/多对一(直接外键)
这里定义用户表和用户类型表,一种用户类型对应多个用户:
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
private String password;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "userInfoId", referencedColumnName = "id")
private UserInfo userInfo;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH})
@JoinColumn(name = "typeId", referencedColumnName = "id")
private UserType userType;
}
@Data
@Entity
@Table(name = "t_user_type")
public class UserType {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String typeName;
private String memo;
// @OneToMany(mappedBy = "userType", cascade = {})
// // 防止json序列化死循环
// @JsonBackReference
// private List<User> userList;
}
测试:
@Test
void testOnetoMany() {
UserType userType = new UserType();
userType.setTypeName("VIP PLUS");
User user = new User();
user.setUsername("lisi");
user.setPassword("123456");
user.setUserType(userType);
userRepository.save(user);
List<User> userList = userRepository.findByUsername("lisi");
Assert.notNull(userList, "获取记录失败");
System.out.println(userList.get(0));
}
4、一对多/多对一(中间表)
这里定义用户表、角色表以及用户角色表,一个角色对应多个用户:
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
private String password;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "userInfoId", referencedColumnName = "id")
private UserInfo userInfo;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH})
@JoinColumn(name = "typeId", referencedColumnName = "id")
private UserType userType;
@ManyToOne(cascade = {})
@JoinTable(name = "t_user_role",
joinColumns = @JoinColumn(name="userId", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "roleId", referencedColumnName = "id"))
private Role role;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", userInfo=" + userInfo +
", userType=" + userType +
'}';
}
}
@Data
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String role;
private String memo;
@OneToMany(cascade = {},targetEntity = User.class,fetch = FetchType.EAGER,mappedBy = "role")
private List<User> userList;
}
@Data
@Entity
@Table(name = "t_user_role")
public class UserRole {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private Integer roleId;
private Integer userId;
}
测试:
@Test
void testOnetoMany2() {
User user = new User();
user.setUsername("wangwu");
user.setPassword("123456");
userRepository.save(user);
User user2 = new User();
user2.setUsername("chenshi");
user2.setPassword("123456");
userRepository.save(user2);
Role role = new Role();
role.setRole("系统管理员");
roleRepository.save(role);
UserRole userRole = new UserRole();
userRole.setRoleId(role.getId());
userRole.setUserId(user.getId());
userRoleRepository.save(userRole);
UserRole userRole2 = new UserRole();
userRole2.setRoleId(role.getId());
userRole2.setUserId(user2.getId());
userRoleRepository.save(userRole2);
Iterable<Role> all = roleRepository.findAll();
Assert.notNull(all, "获取记录失败");
System.out.println(all.iterator().next());
}
5、多对多
这里使用角色表、权限表以及角色权限中间表,权限和角色之间为多对多关系
@Data
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String role;
private String memo;
@ManyToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.EAGER)
@JoinTable(name = "t_role_permission", joinColumns = @JoinColumn(name = "roleId"),
inverseJoinColumns = @JoinColumn(name = "permissionId"))
private List<Permission> permissionList;
}
@Data
@Entity
@Table(name = "t_permission")
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String permission;
private String memo;
@ManyToMany(mappedBy = "permissionList", targetEntity = Role.class,fetch = FetchType.EAGER)
private List<Role> roleList;
@Override
public String toString() {
return "Permission{" +
"id=" + id +
", permission='" + permission + '\'' +
", memo='" + memo + '\'' +
'}';
}
}
@Data
@Entity
@Table(name = "t_role_permission")
public class RolePermission {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private Integer roleId;
private Integer permissionId;
}
测试:
@Test
void testManytoMany() {
Role role1 = new Role();
role1.setRole("账户管理员");
roleRepository.save(role1);
Role role2 = new Role();
role2.setRole("日志管理员");
roleRepository.save(role2);
Role role3 = new Role();
role3.setRole("审批管理员");
roleRepository.save(role3);
Permission permission1 = new Permission();
permission1.setPermission("/user/info");
permissionRepository.save(permission1);
Permission permission2 = new Permission();
permission2.setPermission("/user/list");
permissionRepository.save(permission2);
RolePermission rolePermission1 = new RolePermission();
rolePermission1.setPermissionId(permission1.getId());
rolePermission1.setRoleId(role1.getId());
rolePermissionRepository.save(rolePermission1);
RolePermission rolePermission2 = new RolePermission();
rolePermission2.setPermissionId(permission1.getId());
rolePermission2.setRoleId(role2.getId());
rolePermissionRepository.save(rolePermission2);
RolePermission rolePermission3 = new RolePermission();
rolePermission3.setPermissionId(permission1.getId());
rolePermission3.setRoleId(role3.getId());
rolePermissionRepository.save(rolePermission3);
RolePermission rolePermission4 = new RolePermission();
rolePermission4.setPermissionId(permission2.getId());
rolePermission4.setRoleId(role1.getId());
rolePermissionRepository.save(rolePermission4);
RolePermission rolePermission5 = new RolePermission();
rolePermission5.setPermissionId(permission2.getId());
rolePermission5.setRoleId(role2.getId());
rolePermissionRepository.save(rolePermission5);
RolePermission rolePermission6 = new RolePermission();
rolePermission6.setPermissionId(permission2.getId());
rolePermission6.setRoleId(role3.getId());
rolePermissionRepository.save(rolePermission6);
Iterable<Role> all = roleRepository.findAll();
Assert.notNull(all, "获取记录失败");
all.forEach(e -> {
System.out.println(e);
});
}