<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
server:
port: 8080
servlet:
context-path: /
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_jpa?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
username: root
password: 123456
jpa:
database: MySQL
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
hibernate:
ddl-auto: update
ddl-auto: update
实体类中常用注解:
@Entity :声明这个类是一个实体类
@Table:指定映射到数据库的表格
@Id :映射到数据库表的主键属性,一个实体只能有一个属性被映射为主键
@GeneratedValue:主键的生成策略
@Column配置单列属性
@Entity
@Table(name ="tb_user")
@Data
public class User {
@Id
@GenericGenerator(name ="idGenerator",strategy = "uuid")
@GeneratedValue(generator = "idGenerator")
private String id;
@Column(name ="username",unique= true, nullable = false, length = 64)
private String username;
@Column(name = "password", nullable = false,length =64)
private String password;
@Column(name ="email",length = 64)
private String email;
}
主键采用UUID策略
@GenericGenerator是Hibernate提供的主键生成策略注解,注意下面的@GeneratedValue(JPA注解)使用generator= "idGenerator"引用了上面的name = "idGenerator"主键生成策略
一般简单的Demo示例中只会使用@GeneratedValue(strategy = GenerationType.IDENTITY)这种主键自增的策略,而实际数据库中表字段主键类型很少是int型的
JPA自带的几种主键生成策略
- TABLE: 使用一个特定的数据库表格来保存主键
- SEQUENCE: 根据底层数据库的序列来生成主键,条件是数据库支持序列。这个值要与generator一起使用,generator 指定生成主键使用的生成器(可能是orcale中自己编写的序列)
- IDENTITY: 主键由数据库自动生成(主要是支持自动增长的数据库,如mysql)
- AUTO: 主键由程序控制,也是GenerationType的默认值
Repository: 最顶层的接口,是一个空接口,目的是为了统一所有的Repository的类型,且能让组件扫描时自动识别
CrudRepository: Repository的子接口,提供CRUD 的功能。
PagingAndSortingRepository:CrudRepository的子接口, 添加分页排序。
JpaRepository: PagingAndSortingRepository的子接口,增加批量操作等。
JpaSpecificationExecutor: 用来做复杂查询的接口。
由图来看,一般使用JpaRepository这个接口做查询即可.这个接口拥有如下方法:
1. delete删除或批量删除
2. findOne查找单个
3. findAll查找所有
4. save保存单个或批量保存
5. saveAndFlush保存并刷新到数据库
知道这些我们就可以创建repository 了:
@Repository
//User表示该Repository与实体User关联,主键类型为String
public interface UserRepository extends JpaRepository<User, String>{
}
这样就完成了一个基本Repository的创建,可以直接使用其中的方法,而不需要去写实现类。
也可以自定义方法,进行操作: 例
@Repository
public interface UserRepository extends JpaRepository<User, String>, JpaSpecificationExecutor<User>{
//自定义方法,使用Jpa标准化查询语言
User findByUsername(String username);
//使用JPQL查询,注意 JPQL返回的是Object[]类型
@Query(value = "select a.id,a.email from User a where a.username like " + "%?1%")
Object[] findAllByUsernameLike(String username);
//使用原生的sql进行查询
@Query(value = "select count(*) from t_user where t_user.username like " + "%?1%",nativeQuery = true)
Integer countAllByUsername(String username);
}
为了简单我这里省略了Service层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/add")
@Transactional
//添加
public User saveUser() {
User user = new User();
user.setUsername("admin");
user.setPassword("admin123");
user.setEmail("[email protected]");
return userRepository.save(user);
}
//删除
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable("id") String userId) {
userRepository.deleteById(userId);
}
//修改
@PutMapping("/{id}")
public User updateUser(@PathVariable("id") String userId, @RequestBody User user) {
user.setId(userId);
return userRepository.saveAndFlush(user);
}
//按ID查询
@GetMapping("/{id}")
public User getUserInfo(@PathVariable("id") String userId) {
Optional<User> optional = userRepository.findById(userId);
return optional.orElseGet(User::new);
}
//分页查询
@GetMapping("/list")
public Page<User> pageQuery(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
return userRepository.findAll(PageRequest.of(pageNum - 1, pageSize));
}
//分页查询 排序
@GetMapping("/listSort")
public Page<User> pageQuerySort(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
return userRepository.findAll(PageRequest.of(pageNum - 1, pageSize,Sort.Direction.DESC,"id"));
}
//分页查询 条件判断精确查询 Example
@GetMapping("/listUsername")
public Page<User> pageQueryExample(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize){
User user = new User();
//精确匹配
user.setUsername("admin");
//默认会忽略空值的字段
Example<User> example = Example.of(user);
return userRepository.findAll(example,PageRequest.of(pageNum - 1, pageSize));
}
//分页查询 条件判断模糊查询 ExampleMatcher
@GetMapping("/listUsernames")
public Page<User> xx(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize){
User user = new User();
user.setUsername("admin");
user.setPassword("123");
user.setEmail("[email protected]");
//创建匹配器,即如何使用查询条件
ExampleMatcher matcher = ExampleMatcher.matching()//构建对象
.withMatcher("username", ExampleMatcher.GenericPropertyMatchers.startsWith())//模糊查询匹配开头,即{username}%
.withMatcher("password" ,ExampleMatcher.GenericPropertyMatchers.contains())//全部模糊查询,即%{address}%
.withIgnorePaths("email");//忽略字段,即不管password是什么值都不加入查询条件
//默认会忽略空值的字段
Example<User> example = Example.of(user,matcher);
return userRepository.findAll(example,PageRequest.of(pageNum - 1, pageSize));
}
//自定义 使用Jpa标准化查询语言
@GetMapping("/findUsername")
public User findByUsername() {
return userRepository.findByUsername("admin");
}
//自定义 使用 JPQL
@GetMapping("/findList")
public Object[] findAllByUsernameLike() {
return userRepository.findAllByUsernameLike("admin");
}
//自定义 使用原生的sql进行查询
@GetMapping("/findCount")
public Integer countAllByUsername() {
return userRepository.countAllByUsername("admin");
}
}
注意:save需要添加事务才会保存在数据库中
Example
Example api的组成
Probe: 含有对应字段的实例对象。
ExampleMatcher:ExampleMatcher携带有关如何匹配特定字段的详细信息,相当于匹配条件。
Example:由Probe和ExampleMatcher组成,用于查询。
限制
属性不支持嵌套或者分组约束,比如这样的查询 firstname = ?0 or (firstname = ?1 and lastname = ?2)
灵活匹配只支持字符串类型,其他类型只支持精确匹配
ExampleMatcher
ExampleMatcher实例查询三要素
1. 实体对象:在ORM框架中与Table对应的域对象,一个对象代表数据库表中的一条记录,如上例中User对象,对应user表。在构建查询条件时,一个实体对象代表的是查询条件中的“数值”部分。如:要查询姓“X”的客户,实体对象只需要存储条件值“X”。
2. 匹配器:ExampleMatcher对象,它是匹配“实体对象”的,表示了如何使用“实体对象”中的“值”进行查询,它代表的是“查询方式”,解释了如何去查的问题。如:要查询姓“X”的客户,即姓名以“X”开头的客户,该对象就表示了“以某某开头的”这个查询方式,如上例中:withMatcher(“userName”, GenericPropertyMatchers.startsWith())
3. 实例:即Example对象,代表的是完整的查询条件。由实体对象(查询条件值)和匹配器(查询方式)共同创建。最终根据实例来findAll即可。
ExampleMatcher 静态方法
方法 | 说明 | 演示 |
---|---|---|
matching() | 返回一个匹配所有字段的ExampleMatcher对象;源码调用的是matchingAll()方法 | ExampleMatcher.matching() |
matchingAll() | 返回一个匹配所有字段的ExampleMatcher对象 | ExampleMatcher.matchingAll() |
matchingAny() | 对任意一个字段进行匹配 | ExampleMatcher.matchingAny() |
ExampleMatcher方法
方法 | 说明 | 返回类型 |
---|---|---|
getIgnoredPaths() | 返回所有设置忽略匹配 withIgonrePaths(String… ignorePaths) 的字段集合 | Set |
getMatchMode() | 返回匹配模式(ALL ANY) | ExampleMatcher.MatchMode |
getNullHandler() | 获取一个空处理的ExampleMatcher | ExampleMatcher.NullHandler |
isAllMatching() | 判断匹配模式受否为matchingAll()模式 | boolean |
isAnyMatching() | 判断匹配模式受否为matchingAny()模式 | boolean |
isIgnoreCaseEnabled() | 判断是否开启了忽略大小写模式 | boolean |
isIgnoredPath(String path) | 对一个字段判断是否是设置了忽略匹配模式;也就是设置了 withIgonrePaths(String… ignorePaths) 的字段 | boolean |
withIgnoreCase(boolean defaultIgnoreCase) | 返回一个默认的ExampleMatcher,默认是忽略大小写的 | ExampleMatcher |
withIgnoreCase(String… propertyPaths) | 对一个或多个字段设置忽略大小写 | ExampleMatcher |
withIgonrePaths(String… ignorePaths) | 对一个或多个字段设置,则此字段不受其它匹配影响,也就是说其他任何匹配模式不对这个字段生效 | ExampleMatcher |
withIgnoreNullValues() | 返回一个对被忽略字段 Null空值处理的ExampleMatcher对象 | ExampleMatcher |
withIncludeNullValues() | 返回一个对字段 Null空值处理的ExampleMatcher对象 | ExampleMatcher |
ExampleMatcher.GenericPropertyMatchers静态方法
方法 | 说明 | 返回类型 |
---|---|---|
contains() | 模糊包含匹配 | ExampleMatcher.GenericPropertyMatcher |
endsWith() | 后缀模糊匹配 | ExampleMatcher.GenericPropertyMatcher |
exact() | 精确匹配 | ExampleMatcher.GenericPropertyMatcher |
ignoreCase(boolean ignoreCase) | 设置忽略大小写为 true;也就是忽略大小写 | ExampleMatcher.GenericPropertyMatcher |
caseSensitive() | 设置忽略大小写为 false;也就是不忽略大小写 | ExampleMatcher.GenericPropertyMatcher |
startsWith() | 对开头的字符串模糊匹配 | ExampleMatcher.GenericPropertyMatcher |
storeDefaultMatching() | 默认匹配模式 | ExampleMatcher.GenericPropertyMatcher |
regex() | 将字符串视为正则表达式模式进行匹配 | ExampleMatcher.GenericPropertyMatcher |
of(ExampleMatcher.StringMatcher stringMatcher) | ExampleMatcher.StringMatcher提供了枚举 | ExampleMatcher.GenericPropertyMatcher |
of(ExampleMatcher.StringMatcher stringMatcher, boolean ignoreCase) | 和上一个一样,只是多了一个boolean来指定忽略大小写 | ExampleMatcher.StringMatcher提供了枚 |
ExampleMatcher.StringMatcher提供了如下枚举
CONTAINING | 匹配包含的字符串 |
DEFAULT | 默认匹配模式 |
ENDING | 匹配结尾的字符串 |
EXACT | 匹配精确的字符串 |
REGEX | 将字符串视为正则表达式进行匹配 |
STARTING | 匹配开始的字符串 |