建表语句:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`sex` bit(1) NOT NULL,
`birthday` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
使用 Spring Initializer 添加 web、JPA、 MySQL依赖,生成项目。
application.properties中配置端口、datasource url以及数据库的用户名和密码。
编写实体类:
@Entity
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private Boolean sex;
@Column(name = "birthday")
private Date birth;
}
UserRepository 继承 CrudRepository 接口即可
@Repository
public interface UserRepository extends CrudRepository<User,Integer> {
}
Controller:
@RestController
@RequestMapping("user")
public class HelloWebfluxController {
@Autowired
private UserRepository userRepository;
@GetMapping("/{id}")
public Mono<User> getUser(@PathVariable("id") Integer id){
return Mono.just(userRepository.findById(id).get());
}
@DeleteMapping("/{id}")
public Mono<String> deleteUser(@PathVariable("id") Integer id){
userRepository.deleteById(id);
return Mono.just("success");
}
}
我们需要掌握和使用到的类。
七个Repository接口:
两个实现类:
关系图:
Repository 接口 :只是一个标识。Spring 在动态代理,Repository的子类代表储存库操作。
@Indexed
public interface Repository<T, ID> {
}
CrudRepository接口 : 是Repository接口的子类,提供了通用的CRUD方法。
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
查看 save 方法的实现方法 :
@Transactional
@Override
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
执行插入时,会先判断对象是否存在,再选择是添加或更新。save 方法会执行两条SQL。
查看 delete 方法的实现 :
@Transactional
@Override
public void deleteById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(
String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1)));
}
根据id删除时也会先去查记录是否存在,如果不存在会报 EmptyResultDataAccessException 异常。
PagingAndSortingRepository 接口 : 继承了CrudRepository接口,并添加了 分页、排序方法。
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
Iterable findAll(Sort sort); 可以获取排序好的集合
Page findAll(Pageable pageable); 分页、排序,并用page对象封装结果。
PagingAndSortingRepository 接口默认的实现类是 SimpleJpaRepository。
继承 PagingAndSortingRepository 接口即可使用分页和排序。
@GetMapping("page")
public Page<User> findAll(){
return userRepository.findAll(new PageRequest(1,20,new Sort(Sort.Direction.ASC,"name")));
}
JpaRepository 接口 :继承PagingAndSortingRepository接口。通过源码和CrudRepository相比较, 它支持Query By Example,批量删除, 提高删除效率, 手动刷新数据库的更改方法, 并将默认实现的查询结果变成了List。
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
@Override
List<T> findAll();
@Override
List<T> findAll(Sort sort);
@Override
List<T> findAllById(Iterable<ID> ids);
@Override
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
使用方法也是继承 JpaRepostiory 接口即可。
SimpleJpaRepository 类 :如果想扩展,可以继承这个实现类。
利用方法名,可以定义查询方法。
如:
findByName 就可以根据传入的name进行查询。
findDistinctPeopleByName 根据name查询People并去重
findByNameAndFirstNameAllIngoreCase 忽略大小写
findByLastNameOrderByFirstName 结果按FirstName排序
deleteByName 根据Name删除
使用注解也可以实现查询。
@Query : 声明在查询方法上的注解 :
@Query("select u from User u where name = :name")
User findByName(@Param("name") String name);
注意 : JPQL中写的是实体类,并非是表名,并且大小写也必须一致。
如果JPQL错误,启动时就会报错。
@Query源码:
public @interface Query {
String value() default "";
String countQuery() default "";
String countProjection() default "";
boolean nativeQuery() default false;
String name() default "";
String countName() default "";
}
按示例查询。
User user = new User();
user.setName("杨");
ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith());
Example<User> ex = Example.of(user,matcher);
List<User> all = carrierDao.findAll(ex);
QueryByExampleExcutor特点:
都是围绕着 Specification 定义的,而 Specification 则只有一个方法:
JpaSpecificationExecutor是针对Criteria API进行了predicate标准封装, 帮我们封装了通过EntityManager的查询和使用细节, 操作Criteria更加便利了一些。