目录
1、jpa是什么?
JPA是一个规范,不是框架。
JPA (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。
JPA统一了java应用程序访问ORM框架的规范,结束现在 Hibernate,TopLink,JDO 等 ORM 框架各自为营的局面。
注意:JPA 是在充分吸收了现有 Hibernate,TopLink,JDO 等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。
总的来说,JPA包括以下3方面的技术:
• ORM映射元数据
JPA支持XML和JDK5.0注释(也可译作注解)两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
• Java持久化API
用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者可以从繁琐的JDBC和SQL代码中解脱出来。
• 查询语言(JPQL)
这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
2、spring data jpa
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate 的 JPA 技术实现,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展。
优点:丰富的API,简单操作无需编写额外的代码;丰富的SQL日志输出
缺点:学习成本较大,需要学习HQL;配置复杂,虽然SpringBoot
简化的大量的配置,关系映射多表查询配置依旧不容易;
性能较差,对比JdbcTemplate
、Mybatis
等ORM框架,它的性能是最差的;
3、添加依赖
接下来步入正题
<!-- Spring JDBC 的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MYSQL包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
4、修改配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_study?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
## JPA配置
#spring.jpa.hibernate.ddl-auto=update
# 打印sql语句日志
spring.jpa.show-sql=true
,下面简单介绍下spring.jpa.properties.hibernate.hbm2ddl.auto
有几种配置:
- create:每次加载Hibernate时都会删除上一次生成的表(包括数据),然后重新生成新表,即使两次没有任何修改也会这样执行。适用于每次执行单测前清空数据库的场景。
- create-drop:每次加载Hibernate时都会生成表,但当SessionFactory关闭时,所生成的表将自动删除。
- update:最常用的属性值!!!,第一次加载Hibernate时创建数据表(前提是需要先有数据库),以后加载Hibernate时不会删除上一次生成的表,会根据实体更新,只新增字段,不会删除字段(即使实体中已经删除)。
- validate:每次加载Hibernate时都会验证数据表结构,只会和已经存在的数据表进行比较,根据model修改表结构,但不会创建新表。
- springboot_study是数据库的名称,请自己建
如果不配置此项,表示禁用自动建表功能;笔者建议禁用该功能,实体映射成表可能会出现异常,推荐大家自己手动建表
5、手动建表
user.sql
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`username` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user` (`id`, `username`, `age`) VALUES (1, 'aaa', 18);
INSERT INTO `user` (`id`, `username`, `age`) VALUES (2, 'bbb', 18);
INSERT INTO `user` (`id`, `username`, `age`) VALUES (3, 'ccc', 20);
INSERT INTO `user` (`id`, `username`, `age`) VALUES (4, 'ddd', 24);
6、实体类
新建User类:
package com.zj.springbootstudy.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @Auther: zj
* @Date: 2019/1/19 17:58
* @Description:
*/
@Entity
//@Table
//@Version
public class User {
@Id
//@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
private Integer id;
//@Column
private String username;
//@Column
//@Transient
private Integer age;
//注意:无参构造函数不能省!!!
protected User() { JPA 的规范要求无参构造函数;设为 protected 防止直接使用
}
public User(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
//下面是getter,setter方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() { //重写toString() 方法,便于打印消息
return String.format("User[id=%d, username='%s', age=%d]",
id, username, age);
}
}
@Entity 表示 对实体注释。任何Hibernate映射对象都要有这个注释,很重要
@Id 声明此属性为主键。该属性值可以通过应该自身创建,也可以通过Hibernate自动生成但推荐手动建表
拓展:
1、@Table 声明此对象映射到数据库的数据表,通过它可以为实体指定表(talbe),目录(Catalog)和schema的名字。该注释不是必须的,如果没有则系统使用默认值(实体的短类名)
源码如下:
2、@Version 该注释可用于在实体Bean中添加乐观锁支持
3、@GeneratedValue 指定主键的生成策略
有如下四个值:
TABLE:使用表保存id值,容器指定用底层的数据表确保唯一;
IDENTITY:identitycolumn 使用数据库德SEQUENCE列莱保证唯一(Oracle数据库通过序列来生成唯一ID);
SEQUENCR :sequence 使用数据库的IDENTITY列莱保证唯一
AUTO:根据数据库的不同自动使用上面三个
4、@Column 声明该属性与数据库字段的映射关系。
我们点进去看她的源码:
可以看出,该注解有许多属性
1)name:映射的列名。如:映射tbl_user表的name列,可以在name属性的上面或getName方法上面加入;
2)unique:是否唯一;
3)nullable:是否允许为空;
4)length:对于字符型列,length属性指定列的最大字符长度;
5)insertable:是否允许插入;
6)updatetable:是否允许更新;
7)columnDefinition:定义建表时创建此列的DDL;
5、@Transient 如果不想让一些成员属性映射成数据库字段,可以使用@Transient注释进行标注。
6、@OneToMany,@ManyToMany,@OneToOne,@ManyToOne 声明关联关系,外键关联
一般和@JoinTable配合使用
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER) //这里一定要立即加载,不然会报错:org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role。。。。
// 因为在获取一方的时候一方是获取到了,但是再获取多方的时候session已经关闭了,这时候会获取不到多方信息,因此报错。
@JoinTable(
name = "sys_user_role",
joinColumns = @JoinColumn(
name = "user_id",referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(
name = "role_id",referencedColumnName = "id"
)
)
private List<SysRole> roles;
如非必要,不建议使用外键关联,比较危险
7、创建数据访问接口(数据访问层)
public interface UserRepository extends JpaRepository<User,Integer>{
}
我们的UserRepository继承JpaRepository<User,Integer>这个接口,
发现后面的泛型是什么东东?
查看JpaRepository源码:
由此我们发现,User是我们现前创的类,Integer是主键的类型
还发现JpaRepository默认预先生成了一些基本的CURD的方法,例如:创建(save)、更新(save)、删除(delete)、查询(findAll、findOne)等等
自定义的简单查询
根据官方文档所说,还可以自定义的简单查询就是根据方法名来自动生成SQL,
修改UserRepository:
public interface UserRepository extends JpaRepository<User,Integer>{
User findByUsername(String username);
User findByAge(Integer age);
}
单元测试:我们一般写完一个方法,就需编写对应的单元测试来验证编写的内容是否正确
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
public void findByUsername() throws Exception {
System.out.println( userRepository.findByUsername( "aaa" ));
}
}
结果:
表明测试无误,接下来的例子就不贴了
也可以使用一些加一些关键字And
、 Or
User findByUsernameOrAge(String username, Integer age );
修改、删除、统计也是类似语法
void deleteById(Long id);
Long countByUsername(String username)
基本上SQL体系中的关键词都可以使用,例如:LIKE
、 IgnoreCase
、 OrderBy等等
List<User> findByUsernameLike(String username);
User findByUsernameIgnoreCase(String username);
List<User> findByUserNameOrderByAgeDesc(String age);
jpa一般规则:
这就是Spring-data-jpa的一大特性:通过解析方法名创建查询。
除了通过解析方法名来创建查询外,它也提供通过使用@Query 注解来创建查询,您可以手写sql语句
---
@Query("select u from User u where u.username = username")
User findByUsername2(String username);
--
分页查询
修改UserRepository,添加下方的代码:
Page<User> findAll(Pageable pageable);
Pageable
是spring封装的分页实现类,查看其源码发现
使用的时候可以传入页数、每页条数和排序规则等
单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
public void findAll() throws Exception {
Sort sort = new Sort( Sort.Direction.DESC, "id");
// Pageable pageable = new PageRequest(0, 3,sort); 已经废弃!!!
Pageable pageable = PageRequest.of( 0,3,sort);
System.out.println( userRepository.findAll( pageable ).getContent() );
}
}
结果:
注意:
PageRequest 已经废弃,现在是PageRequest,我们查看其源码:
page传入页数、size每页条数和sort排序规则
8、server层
新建文件目录
这里只举一个例子,其他类推
UserService:
public interface UserService {
List<User> listAllUserWithPage(Pageable pageable);
}
UserServiceImpl:
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserRepository userRepository;
@Override
public List<User> listAllUserWithPage(Pageable pageable) {
return userRepository.findAll( pageable ).getContent();
}
}
9、controller层
@RestController
public class UserController {
@Autowired
UserService userService;
@GetMapping("/test")
public Object test11111() {
Sort sort = new Sort( Sort.Direction.DESC, "id");
Pageable pageable = PageRequest.of( 0,3,sort);
return userService.listAllUserWithPage( pageable);
}
}
测试:
参考:
https://www.cnblogs.com/lich/archive/2011/11/27/2265253.html
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/