spring data jpa

微信截图_20220911160204

spring data jpa

ORM

ORM(Object-Relational Mapping) 表示对象关系映射,就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的

使用ORM的好处

使用ORM则会大大减少重复性代码。对象关系映射,主要实现程序对象到关系数据库数据的映射

常见的ORM对象

  • Mybatis(ibatis)

  • Hibernate

  • Jpa

JPA

全称是Java Persistence API,Java 持久化API,SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类

JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中

最大限度的使用面向对象的模型设计应用,而不需要自行处理这些特性在关系数据库的持久化

JPA与hibernate的关系

JPA规范本质上就是一种ORM规范,不是ORM框架,因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,具体实现则由服务厂商来提供实现

JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作

JPA入门使用

创建表

  • pom
1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
  • 实体类(User)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Data
@Entity
@Table(name = "t_user")
public class User {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@Column(name = "username")
private String userName;

@Column(name = "password")
private String passWord;

}

@Data lombok的SetGet

@Entity 实体类

@Table(name = "t_user") 映射哪个表

@Id 声明这个属性的这个字段是主键

@Column(name = "id") 这个属性映射那个字段

@GeneratedValue(strategy = GenerationType.IDENTITY) 设置策略,策略如下

GenerationType

GenerationType.IDENTITY:底层数据库必须支持自动增长(类似于mysql的自增)

GenerationType.SEQUENCE:底层数据库必须支持序列,(Oracle)

GenerationType.TABLE:jpa提供的一种机制,通过一张数据表的形式帮助完成主键自增

GenerationType.AUTO:程序自动选择合适的主键生成策略

  • 配置(yml)
1
2
3
4
5
6
7
8
9
10
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/jpa?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: xiaobo
jpa:
show-sql: true
hibernate:
ddl-auto: update

ddl-auto: 自动表定义,实现自动在数据库中为我们创建一个表,表的结构会根据我们定义的实体类决定

create 启动时删数据库中的表,然后创建,退出时不删除数据表

create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错

update 如果启动时表格式不一致则更新表,原有数据保留

validate 项目启动表结构进行校验 如果不一致则报错

操作表

实现JpaRepository接口

  • UserRepository
1
2
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {}
  • 测试(crud)
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
32
33
34
35
36
37
38
@Test
public void insert(){

User user = new User();
user.setId(3);
user.setUserName("wangyibo");
user.setPassWord("xiaobo");

userRepository.save(user);

userQuery();
}

@Test
public List<User> userQuery(){

List<User> all = userRepository.findAll();

System.out.println(all);

return all;
}

@Test
public void userDelete(){

userRepository.deleteById(1);

userQuery();

}

@Test
public void userPageable(){

userRepository.findAll(PageRequest.of(0,3)).forEach(System.out::println);

}

方法命名规则

方法命名规则查询就是根据方法的名字,就能创建查询,按照Spring Data JPA 定义的规则写方法名

查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

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
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {


/**
* 根据用户名进行查询
* @param userName
* @return
*/
List<User> findAllByUserName(String userName);


/**
* 根据用户名和密码进行查询
* @param username
* @param password
* @return
*/
List<User> findAllByUserNameAndPassWord(String username, String password);


/**
* 根据用户名进行模糊查询
* @param username
* @return
*/
List<User> findAllByUserNameLike(String username);
}
  • 测试
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
/**
* 根据用户名进行查询
* @return
*/
@Test
public void findAllByUsername(){

userRepository.findAllByUserName("wangyibo").forEach(System.out::println);

}


/**
* 根据用户名和密码进行查询
* @return
*/
@Test
public void findAllByUsernameAndPassword(){
userRepository.findAllByUserNameAndPassWord("wangyibo","xiaobo").forEach(System.out::println);
}

/**
* 根据用户名进行模糊查询
* @return
*/
@Test
public void findAllByUsernameLike(){

userRepository.findAllByUserNameLike("x%").forEach(System.out::println);

}

JPQL的方式

只需在方法上面标注@Query该注解,同时提供一个JPQL查询语句即可

用法

  • 大多数情况下将 * 替换为别名

  • 表名改为类名

  • 字段名改为属性名

  • 搭配注解@Query进行使用

  • userRepositry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Query("SELECT u FROM User u")
List<User> findAllUser();

/**
* 自定义根据用户名查询并传入参数,参数用占位符?+1234...替代
* @param userName
* @return
*/
@Query("SELECT u FROM User u WHERE u.userName=?1")
List<User> findAllUserByUserName(String userName);

/**
* 自定义指定字段
* @return
*/
@Query("SELECT u.id FROM User u")
List<Integer> findAllId();
  • test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void findAllUser(){

userRepository.findAllUser().forEach(System.out::println);

}

@Test
public void findAllUserByUserName(){

userRepository.findAllUserByUserName("wangyibo").forEach(System.out::println);

}

@Test
public void findAllId(){

userRepository.findAllId().forEach(System.out::println);

}

原生方式

nativeQuery = true

  • userRepositry
1
2
3
4
5
6
7
8
9
10
11
/**
* 这里用的是原生格式,不是用的JPQL,字段写的都是表的不是类的,并用 nativeQuery = true 开启原生SQL形式
* @param userName
* @param passWord
* @param id
* @return
*/
@Transactional
@Modifying
@Query(value = "UPDATE t_user u SET u.username=?1,u.password=?2 WHERE u.id=?3",nativeQuery = true)
Integer updateUserById(String userName,String passWord,Integer id);
  • test
1
2
3
4
5
@Test
public void updatUserById(){

userRepository.updateUserById("wangyibo","wangyibo",5);
}

一对一

  • Account
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Data
@Entity
@Table(name = "t_account")
public class Account {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@Column(name = "username")
private String userName;

@Column(name = "password")
private String password;

@JoinColumn(name = "detail_id")
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) //设置关联操作为ALL
private AccountDetail accountDetail;

}

@JoinColumn(name = "detail_id") 映射外键

@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 声明是一对一关系,设置关联操作为ALL,以及懒加载

关联方式

ALL:所有操作都进行关联操作

PERSIST:插入操作时才进行关联操作

REMOVE:删除操作时才进行关联操作

MERGE:修改操作时才进行关联操作

常用的是ALL

  • AccountDetail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
@Entity
@Table(name = "t_account_detail")
public class AccountDetail {

@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)//还是设置一个自增主键
@Id
int id;

@Column(name = "address")
String address;

@Column(name = "email")
String email;

@Column(name = "phone")
String phone;

@Column(name = "real_name")
String realName;

}
  • test
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Test
public void insertAccount(){

Account account=new Account();
account.setUserName("xiaobo");
account.setPassword("xiaobo");

AccountDetail accountDetail=new AccountDetail();
accountDetail.setPhone("15841018742");
accountDetail.setRealName("管理员");
accountDetail.setAddress("china");
accountDetail.setEmail("codexiaobo@163.com");

account.setAccountDetail(accountDetail);

accountRepository.save(account);
}

@Test
public void insertArticle(){

Author author = new Author();
author.setName("xiaobo");
Author save = authorRepository.save(author);

Article article = new Article();
article.setTitle("文章1");
article.setContent("文章1内容");
article.setAuthor(save);

articleRepository.save(article);

Article article2 = new Article();
article2.setTitle("文章2");
article2.setContent("文章2内容");
article2.setAuthor(save);
articleRepository.save(article2);

}

@Test
public void articleDelete(){

articleRepository.deleteById(2L);

}

一对多

@OneToMany

建立一对多的关系映射

属性

targetEntityClass:指定多的多方的类的字节码

mappedBy:指定从表实体类中引用主表对象的名称。

cascade:指定要使用的级联操作

fetch:指定是否采用延迟加载

orphanRemoval:是否使用孤儿删除

关系

使用@OneToMany@ManyToOne来标识一对多的双向关联。一端使用@OneToMany,多端使用@ManyToOne

一对多的双向关系由多端来维护。就是说多端为关系维护端,负责关系的增删改查。一端则为关系被维护端,不能维护关系。

一端使用@OneToMany注释的mappedBy="author"属性表明Author是关系被维护端。

多端使用@ManyToOne@JoinColumn来注释属性,@ManyToOne表明是多端,@JoinColumn设置在表中的关联字段(外键)

演示

  • author
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Data
@Entity
@Table(name = "t_author")
public class Author {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "name")
private String name;

@OneToMany(mappedBy = "author",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
//级联保存、更新、删除、刷新;延迟加载。当删除用户,会级联删除该用户的所有文章
//拥有mappedBy注解的实体类为关系被维护端
//mappedBy="author"中的author是Article中的author属性
private List<Article> articleList;

}
  • article
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Data
@Entity
@Table(name = "t_article")
@ToString(exclude = {"author"})
public class Article {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
private Long id;

@Column(nullable = false, length = 50,name = "title") // 映射为字段,值不能为空
private String title;

@Lob // 大对象,映射 MySQL 的 Long Text 类型
@Basic(fetch = FetchType.LAZY) // 懒加载
@Column(nullable = false,name = "content") // 映射为字段,值不能为空
private String content;//文章全文内容

@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)//可选属性optional=false,表示author不能为空。删除文章,不影响用户
@JoinColumn(name="author_id")//设置在article表中的关联字段(外键)
private Author author;//所属作者

}
  • test
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Test
public void insertArticle(){

Author author = new Author();
author.setName("xiaobo");
Author save = authorRepository.save(author);

Article article = new Article();
article.setTitle("文章1");
article.setContent("文章1内容");
article.setAuthor(save);

articleRepository.save(article);

Article article2 = new Article();
article2.setTitle("文章2");
article2.setContent("文章2内容");
article2.setAuthor(save);
articleRepository.save(article2);

}

@Test
public void articleDelete(){

articleRepository.deleteById(2L);

}

@Test
public void queryArticle(){


Optional<Author> byId = authorRepository.findById(1L);

if (byId.isPresent()) {

Author author = byId.get();

List<Article> articleList = author.getArticleList();

System.out.println(articleList);

}
}

多对多

多对多关系中一般不设置级联保存、级联删除、级联更新等操作

可以随意指定一方为关系维护端

多对多关系的绑定由关系维护端来完成,关系被维护端不能绑定关系

多对多关系的解除由关系维护端来完成,关系被维护端不能解除关系

如果维护端和被维护端已经绑定了关系,不能直接删除被维护端,需要维护端解除关系后,才能删除,但是可以直接删除维护端,删除时会先自动解除二者关系,然后再删除被维护端

演示

  • UserMore
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
32
@Data
@Entity
@Table(name = "t_usermore")
public class UserMore {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "username")
private String username; // 用户账号,用户登录时的唯一标识


@Column(name = "password")
private String password; // 登录时密码

@ManyToMany
@JoinTable(name = "t_user_authority",joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "authority_id"))
//1、关系维护端,负责多对多关系的绑定和解除
//2、@JoinTable注解的name属性指定关联表的名字,joinColumns指定外键的名字,关联到关系维护端(User)
//3、inverseJoinColumns指定外键的名字,要关联的关系被维护端(Authority)
//4、其实可以不使用@JoinTable注解,默认生成的关联表名称为主表表名+下划线+从表表名,
//即表名为user_authority
//关联到主表的外键名:主表名+下划线+主表中的主键列名,即user_id
//关联到从表的外键名:主表中用于关联的属性名+下划线+从表的主键列名,即authority_id
//主表就是关系维护端对应的表,从表就是关系被维护端对应的表
private List<Authority> authorityList;


}
  • Authority
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@Entity
@Table(name = "t_authority")
public class Authority {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "name")
private String name; //权限名

@ManyToMany(mappedBy = "authorityList")
private List<UserMore> userList;
}
  • test
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
32
33
@Test
public void insertAuthority(){

Authority authority = new Authority();

authority.setName("admin");

authorityRepository.save(authority);

}

@Test
public void insertUserMore(){

UserMore userMore = new UserMore();

userMore.setUsername("xiaobo");
userMore.setPassword("xiaobo");

List<Authority> authorityList = authorityRepository.findAll();

userMore.setAuthorityList(authorityList);

userMoreRepository.save(userMore);

}

@Test
public void deleteUserMore(){

userMoreRepository.deleteById(2L);

}

正确的开始,微小的长进,然后持续,嘿,我是小博,带你一起看我目之所及的世界……

-------------本文结束 感谢您的阅读-------------

本文标题:spring data jpa

文章作者:小博

发布时间:2022年09月13日 - 17:54

最后更新:2022年09月13日 - 17:55

原始链接:https://codexiaobo.github.io/posts/1272942207/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。