JPA是什么

JPA即Java Persistence API。是为POJO(Plain Ordinary Java Object)提供持久化的标准规范。

简单来说,使用基于JPA标准实现的框架可以实现对象与数据源的绑定,并将运行期的实体对象持久化到数据库中。

为什么要使用JPA

通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

解放双手,不用关心如何使用SQL语句操作数据库,让程序猿更关注业务本身。

什么是spring data jpa

spirng data jpa是spring提供的一套简化JPA开发的框架,基于Hibernate实现的JPA的进一步抽象封装。

如何使用

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

配置文件

在application.yml文件中添加如下配置

1
2
3
4
5
6
7
8
9
10
11
spring:
datasource:
name: springbootdemo #数据库名
url: jdbc:mysql://localhost:3306/springbootdemo
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver #数据库链接驱动
jpa:
hibernate:
ddl-auto: update #自动更新
show-sql: true #日志中显示sql语句

实体类配置

数据库与实体类的映射需要在实体类添加 @Entity 注释。

如需指定映射表的名称,则需要指定 @Table(name=”user”) 否则将根据实体类名称自动创建表,如UserBean类会自动生成user_bean表作为映射。

在类的属性上需要增加相关注释进行配置,类和属性的全部注释如下

注解 解释
@Entity 声明类为实体或表。
@Table 声明表名。
@Basic 指定非约束明确的各个字段。
@Embedded 指定类或它的值是一个可嵌入的类的实例的实体的属性。
@Id 指定的类的属性,用于识别(一个表中的主键)。
@GeneratedValue 指定如何标识属性可以被初始化,例如自动、手动、或从序列表中获得的值。
@Transient 指定的属性,它是不持久的,即:该值永远不会存储在数据库中。
@Column 指定持久属性栏属性。
@SequenceGenerator 指定在@GeneratedValue注解中指定的属性的值。它创建了一个序列。
@TableGenerator 指定在@GeneratedValue批注指定属性的值发生器。它创造了的值生成的表。
@AccessType                                              这种类型的注释用于设置访问类型。如果设置@AccessType(FIELD),则可以直接访问变量并且不需要getter和setter,但必须为public。如果设置@AccessType(PROPERTY),通过getter和setter方法访问Entity的变量。
@JoinColumn 指定一个实体组织或实体的集合。这是用在多对一和一对多关联。
@UniqueConstraint 指定的字段和用于主要或辅助表的唯一约束。
@ColumnResult 参考使用select子句的SQL查询中的列名。
@ManyToMany 定义了连接表之间的多对多一对多的关系。
@ManyToOne 定义了连接表之间的多对一的关系。
@OneToMany 定义了连接表之间存在一个一对多的关系。
@OneToOne 定义了连接表之间有一个一对一的关系。
@NamedQueries 指定命名查询的列表。
@NamedQuery 指定使用静态名称的查询。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@Data
@ApiModel("用户")
@Table(name="user")
public class UserBean {

@Id
@GeneratedValue
@Column(name="id")//数据库字段名
@ApiModelProperty("用户id")
@NotNull(message = "用户id不能为空")
private int id;

@NotNull(message = "用户账号不能为空")
@Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
@Column(name = "name", nullable = true, length = 20)
private String name;

@NotNull(message = "用户密码不能为空")
@Size(min = 6, max = 11, message = "密码长度必须是6-16个字符")
@Column(name = "password", nullable = true, length = 20)
private String password;

}

Repository类

只需要继承JpaRepository接口即可:

1
2
3
4
@Repository
public interface UserMapper extends JpaRepository<UserBean,Integer> {

}

此时UserMapper有很多增删改查的默认API可以调用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();

List<T> findAll(Sort var1);

List<T> findAllById(Iterable<ID> var1);

<S extends T> List<S> saveAll(Iterable<S> var1);

void flush();

<S extends T> S saveAndFlush(S var1);

void deleteInBatch(Iterable<T> var1);

void deleteAllInBatch();

T getOne(ID var1);

<S extends T> List<S> findAll(Example<S> var1);

<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

但是我们需求肯定是不止这些的,所以要自定义一些API,Spring提供了一套可以通过命名规则进行查询构建的机制。

这套机制会把方法名首先过滤一些关键字,比如 find…By, read…By, query…By, count…By 和 get…By。

系统会根据关键字将命名解析成2个子语句,第一个 By 是区分这两个子语句的关键词。这个 By 之前的子语句是查询子语句(指明返回要查询的对象),后面的部分是条件子语句。

定义关键词的命名规范和例子如下:

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection age) … where x.age not in ?1
TRUE findByActiveTrue() … where x.active = true
FALSE findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)
……

这里如果我们想通过用户名和密码进行数据查询就可以这样写 findByNameAndPassword。

所以将自定义方法写入:

1
2
3
4
@Repository
public interface UserMapper extends JpaRepository<UserBean,Integer> {
UserBean findByNameAndPassword(String name, String password);
}

Service类接口

我们定义两个服务接口,一个是验证用户账号密码,一个是添加一个用户:

1
2
3
4
5
6
7
8
public interface UserService {

UserBean loginIn(String name, String password);

String addUser(UserBean user);

}

Service类的实现

增加用户:如果成功则返回success;验证用户:查询结果直接进行返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class UserServiceImpl implements UserService {

//将DAO注入Service层
@Autowired
private UserMapper userMapper;

@Override
public String addUser(UserBean user) {
// 直接编写业务逻辑
if (userMapper.save(user) != null)
return "success";
else return "failed";
}

@Override
public UserBean loginIn(String name, String password) {
return userMapper.findByNameAndPassword(name, password);
}
}

Controller层接口

Controller层正常将相关接口写出来,可以忽略@Valid BindingResult等操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RestController
public class LoginController {

//将Service注入Web层
@Autowired
UserService userService;

@ApiOperation("添加用户")
@PostMapping(path = "addUser")
public String addUser(@RequestBody @Valid UserBean user, BindingResult bindingResult) {
// 如果有参数校验失败,会将错误信息封装成对象组装在BindingResult里
for (ObjectError error : bindingResult.getAllErrors()) {
return error.getDefaultMessage();
}
return userService.addUser(user);
}


@ApiOperation("登陆")
@RequestMapping(value = "/loginIn",method = RequestMethod.POST)
public String login(String name,String password){
UserBean userBean = userService.loginIn(name,password);
if(userBean!=null){
return "success";
}else {
return "error";
}
}

}

接口验证

增加用户:

登陆:

控制台可以看到相关数据库执行日志:

1
2
3
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into user (name, password, id) values (?, ?, ?)
Hibernate: select userbean0_.id as id1_0_, userbean0_.name as name2_0_, userbean0_.password as password3_0_ from user userbean0_ where userbean0_.name=? and userbean0_.password=?

数据库中相关数据已经添加: