JPA介绍
JPA(Java Persistence API)和JDBC类似,也是官方定义的一组接口,但是它相比传统的JDBC,它是为了实现ORM而生的,即Object-Relationl Mapping,它的作用是在关系型数据库和对象之间形成一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。
在之前,我们使用JDBC或是Mybatis来操作数据,通过直接编写对应的SQL语句来实现数据访问,但是我们发现实际上我们在Java中大部分操作数据库的情况都是读取数据并封装为一个实体类,因此,为什么不直接将实体类直接对应到一个数据库表呢?也就是说,一张表里面有什么属性,那么我们的对象就有什么属性,所有属性跟数据库里面的字段一一对应,而读取数据时,只需要读取一行的数据并封装为我们定义好的实体类既可以,而具体的SQL语句执行,完全可以交给框架根据我们定义的映射关系去生成,不再由我们去编写,因为这些SQL实际上都是千篇一律的。
配置文件编写
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://localhost:3306/jpa
username: root
jpa:
#开启SQL语句执行日志信息
show-sql: true
hibernate:
#配置为自动创建
ddl-auto: create
声明连接的是MySQL数据库。
导入依赖
org.springframework.boot spring-boot-starter-data-jpa实体注解
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@Entity
@Table(name = "orm_user")
@ToString(callSuper = true)
public class User extends AbstractAuditModel {
/**
* 用户名
*/
private String name;
/**
* 加密后的密码
*/
private String password;
/**
* 加密使用的盐
*/
private String salt;
/**
* 邮箱
*/
private String email;
/**
* 手机号码
*/
@Column(name = "phone_number")
private String phoneNumber;
/**
* 状态,-1:逻辑删除,0:禁用,1:启用
*/
private Integer status;
/**
* 上次登录时间
*/
@Column(name = "last_login_time")
private Date lastLoginTime;
/**
* 关联部门表
* 1、关系维护端,负责多对多关系的绑定和解除
* 2、@JoinTable注解的name属性指定关联表的名字,joinColumns指定外键的名字,关联到关系维护端(User)
* 3、inverseJoinColumns指定外键的名字,要关联的关系被维护端(Department)
* 4、其实可以不使用@JoinTable注解,默认生成的关联表名称为主表表名+下划线+从表表名,
* 即表名为user_department
* 关联到主表的外键名:主表名+下划线+主表中的主键列名,即user_id,这里使用referencedColumnName指定
* 关联到从表的外键名:主表中用于关联的属性名+下划线+从表的主键列名,department_id
* 主表就是关系维护端对应的表,从表就是关系被维护端对应的表
*/
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "orm_user_dept", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "dept_id", referencedColumnName = "id"))
private Collection<Department> departmentList;
}
实体注解
@EqualsAndHashCode(callSuper = true):生成equals和hashCode方法时调用父类的同名方法。@NoArgsConstructor:生成一个无参的构造方法。@AllArgsConstructor:生成一个包含所有参数的构造方法。@Data:自动生成getter、setter、toString等方法。@Builder:生成一个内部静态类Builder,用于链式调用设置对象属性。@Entity:声明这是一个实体类。@Table(name = "orm_user"):指定该实体类对应的数据库表的名称为"orm_user"。@ToString(callSuper = true):生成toString方法时包含父类的属性。
DAO层编写
需要自定义接口继承JpaRepository
@Repository
public interface UserDao extends JpaRepository<User, Long> {
}
注意JpaRepository有两个泛型,前者是具体操作的对象实体,也就是对应的表,后者是ID的类型。 接下来我们仅需在SpringbootTest中进行JPA功能测试。只需要将UserDao对象进行创建,就可以进行crud操作了。
crud操作
@Test
public void testSave() {
String salt = IdUtil.fastSimpleUUID();
User testSave3 = User.builder().name("testSave3").password(SecureUtil.md5("123456" + salt)).salt(salt).email("testSave3@xkcoding.com").phoneNumber("17300000003").status(1).lastLoginTime(new DateTime()).build();
userDao.save(testSave3);
Assert.assertNotNull(testSave3.getId());
Optional<User> byId = userDao.findById(testSave3.getId());
Assert.assertTrue(byId.isPresent());
log.debug("【byId】= {}", byId.get());
}
@Test
public void testDelete() {
long count = userDao.count();
userDao.deleteById(1L);
long left = userDao.count();
Assert.assertEquals(count - 1, left);
}
/**
* 测试修改
*/
@Test
public void testUpdate() {
userDao.findById(1L).ifPresent(user -> {
user.setName("JPA修改名字");
userDao.save(user);
});
Assert.assertEquals("JPA修改名字", userDao.findById(1L).get().getName());
}
/**
* 测试查询单个
*/
@Test
public void testQueryOne() {
Optional<User> byId = userDao.findById(1L);
Assert.assertTrue(byId.isPresent());
log.debug("【byId】= {}", byId.get());
}
/**
* 测试查询所有
*/
@Test
public void testQueryAll() {
List<User> users = userDao.findAll();
Assert.assertNotEquals(0, users.size());
log.debug("【users】= {}", users);
}
- 测试增加,利用userDao.save()方法,将User进行存储进数据库中。
- 测试删除,利用userDao.deleteById(),根据主键id进行删除。
- 测试查找,利用userDao.findById()和userDao.findAll()进行查找。