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入门使用 创建表
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 >
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
:程序自动选择合适的主键生成策略
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接口
1 2 @Repository public interface UserRepository extends JpaRepository <User ,Integer > {}
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 > { List<User> findAllByUserName (String userName) ; List<User> findAllByUserNameAndPassWord (String username, String password) ; 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 @Test public void findAllByUsername () { userRepository.findAllByUserName("wangyibo" ).forEach(System.out::println); } @Test public void findAllByUsernameAndPassword () { userRepository.findAllByUserNameAndPassWord("wangyibo" ,"xiaobo" ).forEach(System.out::println); } @Test public void findAllByUsernameLike () { userRepository.findAllByUserNameLike("x%" ).forEach(System.out::println); }
JPQL的方式 只需在方法上面标注@Query
该注解,同时提供一个JPQL查询语句即可
用法
大多数情况下将 * 替换为别名
表名改为类名
字段名改为属性名
搭配注解@Query进行使用
例
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 () ; @Query("SELECT u FROM User u WHERE u.userName=?1") List<User> findAllUserByUserName (String userName) ; @Query("SELECT u.id FROM User u") List<Integer> findAllId () ;
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
1 2 3 4 5 6 7 8 9 10 11 @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) ;
1 2 3 4 5 @Test public void updatUserById () { userRepository.updateUserById("wangyibo" ,"wangyibo" ,5 ); }
一对一
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) private AccountDetail accountDetail; }
@JoinColumn(name = "detail_id")
映射外键
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
声明是一对一关系,设置关联操作为ALL,以及懒加载
关联方式
ALL
:所有操作都进行关联操作
PERSIST
:插入操作时才进行关联操作
REMOVE
:删除操作时才进行关联操作
MERGE
:修改操作时才进行关联操作
常用的是ALL
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; }
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
设置在表中的关联字段(外键)
演示
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) private List<Article> articleList; }
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 @Basic(fetch = FetchType.LAZY) @Column(nullable = false,name = "content") private String content; @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false) @JoinColumn(name="author_id") private Author author; }
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); } }
多对多 多对多关系中一般不设置级联保存、级联删除、级联更新等操作
可以随意指定一方为关系维护端
多对多关系的绑定由关系维护端来完成,关系被维护端不能绑定关系
多对多关系的解除由关系维护端来完成,关系被维护端不能解除关系
如果维护端和被维护端已经绑定了关系,不能直接删除被维护端,需要维护端解除关系后,才能删除,但是可以直接删除维护端,删除时会先自动解除二者关系,然后再删除被维护端
演示
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")) private List<Authority> authorityList; }
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; }
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 ); }
正确的开始,微小的长进,然后持续,嘿,我是小博,带你一起看我目之所及的世界……