代码拉取完成,页面将自动刷新
package com.wmx.service.impl;
import com.wmx.config.Md5KeyGenerator;
import com.wmx.entity.Tv;
import com.wmx.repository.TvRepository;
import com.wmx.service.TvService;
import org.springframework.cache.annotation.*;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* 电视机业务层实现
*
* @author wangmaoxiong
*/
@Service
@CacheConfig(cacheNames = {"com.wmx.service.impl.TvServiceImpl"})
public class TvServiceImpl implements TvService {
@Resource
private TvRepository tvRepository;
/**
* 查询表中所有数据
* 在 redis 中缓存的 key 名为:com.wmx.service.impl.TvServiceImpl::findAll
* #root.method.name:表示取当前被调用方法的名称
*
* @return
*/
@Override
@Cacheable(value = "com.wmx.service.impl.TvServiceImpl", key = "#root.method.name")
public List<Tv> findAll() {
System.out.println("查询所有数据.");
//org.springframework.data.jpa.repository.JpaRepository.findAll():没有数据时,返回空列表,不会为null
return tvRepository.findAll();
}
/**
* 根据多个主键Id查询
* redis 中缓存的 key 名格式如:com.wmx.service.impl.TvServiceImpl::findAllById#1,2,3,4,5
* #号后面是动态的 ids 参数值
* #root.method.name:表示取当前被调用方法的名称
*
* @param ids
* @return
*/
@Override
@Cacheable(cacheNames = "com.wmx.service.impl.TvServiceImpl", key = "#root.method.name + '#' + #ids")
public List<Tv> findAllById(List<String> ids) {
System.out.println("根据 id列表 查询数据:" + ids);
return tvRepository.findAllById(ids);
}
/**
* 根据主键id查询
* <p>
* redis 缓存的 key 名格式为:com.wmx.service.impl.TvServiceImpl::findById#b40ff47d172d48a9ae5ae21c1a39a7d7
* #号后面是动态的id参数值。
* #root.method.name:表示取当前被调用方法的名称
* #root.args[0] 表示取参数列表中的第一个参数值。
* 默认结果为 null 时也会缓存。
* unless = "#result == null" :表示如果返回结果为 null ,则不进行缓存。
*
* @param id
* @return
*/
@Override
@Cacheable(value = "com.wmx.service.impl.TvServiceImpl", key = "#root.method.name + '#' + #root.args[0]", unless = "#result == null")
public Tv findById(String id) {
Optional<Tv> tvOptional = tvRepository.findById(id);
//判断是否存在. 存在时取值
if (tvOptional.isPresent()) {
return tvOptional.get();
}
return null;
}
/**
* 条件查询单条数据
* 默认 redis 缓存的 key 名格式为:
* com.wmx.service.impl.TvServiceImpl::Tv{tvId=null, tvName='23f582f3-5bfa-482d-88b4-e85037d31406', tvPrice=null, dateOfProduction=null, createTime=null, updateTime=null, deleted=null, version=null}
* 经过自定义缓存key生成策略后缓存的 key 格式如:com.wmx.service.impl.TvServiceImpl::findOne#ktsStmkO5RhW6vr+dGC9aQ==
* <p>
* keyGenerator:指定缓存Key的生成策略,和key属性互斥使用(只能二选一)。值是自定义的缓存策略实例名称。{@link Md5KeyGenerator}
*
* @param tv
* @return
*/
@Override
@Cacheable(value = "com.wmx.service.impl.TvServiceImpl", keyGenerator = "md5KeyGenerator")
public Tv findOne(Tv tv) {
Example<Tv> tvExample = Example.of(tv);
//<S extends T> Optional<S> findOne(Example<S> example):条件查询单条数据
//以 Example 中的实体中的非null字段为条件进行查询。无数据时Optional.isPresent为false
//如果查询的结果多余1条,则 findOne 抛异常:NonUniqueResultException
Optional<Tv> tvOptional = tvRepository.findOne(tvExample);
if (tvOptional.isPresent()) {
return tvOptional.get();
}
return null;
}
/**
* 查询所有并指定排序字段
* 缓存 key 名为:com.wmx.service.impl.TvServiceImpl::findAllSort
* #root.method.name:表示取当前被调用方法的名称
*
* @return
*/
@Override
@Cacheable(value = "com.wmx.service.impl.TvServiceImpl", key = "#root.method.name", sync = true)
public List<Tv> findAllSort() {
//org.springframework.data.domain.Sort:指定排序字段
//by(Direction direction, String... properties):Direction:方向、趋势,properties:排序的字段
//asc:顺序排序,desc:倒序排序
Sort sort1 = Sort.by(Sort.Direction.DESC, "tvId");
//同时指定多个字段进行排序。以 tvId 倒序查询,当 tvId 相同时,使用 tvPrice 升序
Sort sort2 = Sort.by(Sort.Order.desc("tvId"), Sort.Order.asc("tvPrice"));
return tvRepository.findAll(sort2);
}
/**
* 分页查询,以 tvId 倒序排序
* 缓存 key 名格式为:com.wmx.service.impl.TvServiceImpl::findPageable#1,50
* #root.args :表示取整个参数列表的值。
*
* @param page
* @param size
* @return
*/
@Override
@Cacheable(value = "com.wmx.service.impl.TvServiceImpl", key = "'findPageable#' + #root.args", sync = true)
public Page<Tv> findPageable(int page, int size) {
//of(int page, int size, Sort sort):分页请求,page 表示请求的页码,从 0 开始,size 表示每页的条数
//还有 PageRequest of(int page, int size)、of(int page, int size, Direction direction, String... properties)
page--;
page = page < 0 ? 0 : page;
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "tvId"));
//Page<T> findAll(Pageable pageable):分页查询
//查询无数据时,不会有影响,只是数据大小为0而已
return tvRepository.findAll(pageable);
}
/**
* 条件查询,加分页。同时指定以 tvId 倒序排序
* 缓存的 key 格式为:com.wmx.service.impl.TvServiceImpl::findAll#A8ZkLZIY6DAraGBIT5VtGQ==
*
* @param tv
* @param page
* @param size
* @return
*/
@Override
@Cacheable(keyGenerator = "md5KeyGenerator")
public Page<Tv> findAll(Tv tv, int page, int size) {
page--;
page = page < 0 ? 0 : page;
//设置查询条件
Example<Tv> tvExample = Example.of(tv);
//pageable 分页请求可以设置分页以及排序字段
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Order.desc("tvId")));
return tvRepository.findAll(tvExample, pageable);
}
@Override
public Tv getOneById(String id) {
if (tvRepository.existsById(id)) {
//T getOne(ID id):根据id查询单条数据。如果 id 不存在,则抛异常:EntityNotFoundException
return tvRepository.getOne(id);
}
return null;
}
@Override
public List<Tv> findAllExample(Tv tv) {
//static <T> Example<T> of(T probe)
Example<Tv> tvExample = Example.of(tv);
//<S extends T> List<S> findAll(Example<S> example)
// 根据 example 的 probe 中非 null 的字段进行调节查询.不存在时,返回一个空list
//如果实体的所有字段都为null,则默认查询所有。
return tvRepository.findAll(tvExample);
}
@Override
public List<Tv> findAll(Tv tv, Sort sort) {
Example<Tv> tvExample = Example.of(tv);//static <T> Example<T> of(T probe)
//以 tvId 倒序查询,当 tvId 相同时,使用 tvPrice 升序:Sort.by(Sort.Order.desc("tvId"), Sort.Order.asc("tvPrice"));
return tvRepository.findAll(tvExample, sort);
}
@Override
public long count() {
//查询表中的数据条数,表中无数据时,返回0
return tvRepository.count();
}
/**
* 添加或更新
* <p>
* CacheEvict 注解的方法在被调用时,会从缓存中移除已存储的数据。一般用于删除缓存数据,相当于缓存使用的是写模式中的失效模式。
* 属性基本与 @Cacheable 相同,同时多了两个属性:
* allEntries:是否根据缓存名称(cacheNames)清空所有缓存数据,默认值为false,当值指定为true时,将忽略key属性。
* beforeInvocation:是否在方法执行之前就清空缓存,默认值为false。
*
* @param tv
*/
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void saveOrUpdate(Tv tv) {
//如果实体的主键为null,直接新增数据;如果id不为null,则查询数据库是否有此id,有则修改,没果则新增(主键以主键生成策略进行生成)
tvRepository.save(tv);
}
@Override
@Caching(evict = {
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true)
})
public void saveAll(List<Tv> tvList) {
tvRepository.saveAll(tvList);//同时保持与更新多条数据。底层也是调用 save 方法
}
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void deleteById(String id) {
if (tvRepository.existsById(id)) {//先判断是否存在
//如果主键为id 的数据不存在,则抛异常:EmptyResultDataAccessException
tvRepository.deleteById(id);//根据主键Id删除
}
}
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void deleteAll() {
tvRepository.deleteAll();//删除表中所有数据。数据不存在时,也没问题
}
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void delete(Tv tv) {
//本质也是根据实体的主键 id 进行删除
//所以如果设置主键以外的属性是没用的
tvRepository.delete(tv);
}
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void deleteAll(List<Tv> tvList) {
//deleteAll(Iterable<? extends T> entities)
//一次删除多个实体主键对应的多条数据,本质也就是多次的 delete(T entity)。值不存在时,不会影响
tvRepository.deleteAll(tvList);
}
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void deleteInBatch(List<Tv> tvList) {
//deleteInBatch(Iterable<T> entities):根据实体主键批量删除。值不存在时,不影响
tvRepository.deleteInBatch(tvList);
}
@Override
@CacheEvict(value = "com.wmx.service.impl.TvServiceImpl", allEntries = true, beforeInvocation = false)
public void deleteAllBatch() {
//批量删除表中所有数据。表中无数据时,不影响。
tvRepository.deleteAllInBatch();
}
@Override
public List<Tv> findAll(Date start, Date end, String tvName) {
//直接使用匿名内部类实现接口
Specification specification = new Specification<Tv>() {
@Override
public Predicate toPredicate(Root<Tv> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicateList = new ArrayList<>();
//条件1:查询 tvName 为 海信 的数据,root.get 中的值与 Tv 实体中的属性名称对应
if (tvName != null && !"".equals(tvName)) {
predicateList.add(cb.equal(root.get("tvName").as(String.class), tvName));
}
//条件2:Tv 生产日期(dateOfProduction)大于等于 start 的数据,root.get 中的 dateOfProduction 必须对应 Tv 中的属性
predicateList.add(cb.greaterThanOrEqualTo(root.get("dateOfProduction").as(Date.class), start));
//条件3:Tv 生产日期(dateOfProduction)小于等于 end
predicateList.add(cb.lessThanOrEqualTo(root.get("dateOfProduction").as(Date.class), end));
Predicate[] pre = new Predicate[predicateList.size()];
pre = predicateList.toArray(pre);
return query.where(pre).getRestriction();
}
};
//没有数据时,返回空列表
return tvRepository.findAll(specification);
}
@Override
public Page<Tv> findAll(Date start, int page, int size) {
//page 为页码,数据库从0页开始
page--;
page = page < 0 ? 0 : page;
//可以使用重载的 of(int page, int size, Sort sort) 方法指定排序字段
Pageable pageable = PageRequest.of(page, size);
//创建查询规范
Specification<Tv> tvSpecification = new Specification<Tv>() {
@Override
public Predicate toPredicate(Root<Tv> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicateList = new ArrayList<>();
//查询生产日期在 start 与当期时间之间的数据,闭区间
predicateList.add(cb.between(root.get("dateOfProduction").as(Date.class), start, new Date()));
Predicate[] predicates = new Predicate[predicateList.size()];
return query.where(predicateList.toArray(predicates)).getRestriction();
}
};
//无数据时返回空列表
return tvRepository.findAll(tvSpecification, pageable);
}
@Override
public List<Tv> findAllLike(String tvNameLike) {
Specification<Tv> tvSpecification = new Specification<Tv>() {
@Override
public Predicate toPredicate(Root<Tv> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate[] predicates = new Predicate[1];
//like(Expression<String> x, String pattern):参数 pattern 表示匹配的格式
predicates[0] = cb.like(root.get("tvName").as(String.class), "%" + tvNameLike + "%");
//同理以 xxx 开头,则为 tvNameLike + "%"
return query.where(predicates).getRestriction();
}
};
//规范查询的同时,指定以主键 tvId 倒序排序
return tvRepository.findAll(tvSpecification, Sort.by(Sort.Direction.DESC, "tvId"));
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。