一、spring data jpa 框架
- spring data 提供了基于 spring 的使用习惯(方式)来操作各种类型的数据,主要包括各种数据库及 no sql。因此它的适用面比 mybatis 大很多。
- 使用数据访问技术、关系和非关系数据库(no sql)、mapreduce 框架和基于云的数据库变得容易。这是一个伞形项目,其中包含许多特定与给定数据库的子项目。
- spring data 有很多子项目,其中 commons 和 jdbc 是基础,其他的针对应用的包括 jpa,mongodb,redis,elasticsearch 等。
二、jpa
- jpa 是 java persistence api 也就是 java 持久化应用接口。javaEE 5.0 中出现的操作数据库的标准,它提供了多个接口,比如 @Table、@Id、@Column 等注解都属于该标准的内容。这些内容所在的包在 javax.persistence 中。在 spring boot 项目中如果引入了 spring data jpa 的启动器,该包就会被引用。
- jpa 作为一种标准,被很多 orm 的框架所遵循,典型的是 hibernate 框架,另一个就是 spring data jpa, spring 是基于 java 语言的框架,所以它的 orm 框架遵循 jpa 标准是理所当然的。mybatis 并没有实现 jpa 标准。这两个框架各有千秋,mybatis 更优秀的在于书写复杂的查询:spring data jpa 更优秀在于通过方法名来生成不同的 sql。
三、spring data jpa 在 spring boot 中的使用
-
启动器 spring-boot-starter-data-jpa,基于启动器 spring,jdbc,spring orm,jpa 等相关的依赖都会被引入使用。
-
配置文件中,可以添加 spring.jpa 的相关配置内容:jpa:database:mysql。因为需要根据特定数据库生成 sql。
-
创建 pojo 类,需要添加 jpa 的 Entity,Table,Id 等注解。
-
建 dao 包,在包下建接口。
-
接口需要继承 JpaRepository 接口,该接口可以实现针对单表的几乎所有的 crud 操作,可以分页。还需要继承 JpaSpecificationExecutor 独立接口,它可以实现复杂条件的单表查询,也可以分页。
-
实现 service 类。
-
创建 controller 类,按照 api 文档书写各个请求处理方法。在控制器类中需要考虑跨域请求的问题,否则报错。解决办法:在控制器类上添加 @CrossOrigin 注解。
-
基于组合条件查询。
-
package com.tensquare.base.dao; import com.tensquare.base.pojo.Label; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author 华韵流风 * @ClassName LabelDao * @Date 2021/9/17 17:11 * @packageName com.tensquare.base.dao * @Description TODO */ public interface LabelDao extends JpaRepository<Label,String>, JpaSpecificationExecutor<Label> { }
-
/** * 标签综合查询 * * @param label label * @return List<Label> */ public List<Label> findByLabel(Label label) { List<Predicate> list = new ArrayList<>(); return labelDao.findAll((Specification<Label>) (root, criteriaQuery, criteriaBuilder) -> { if (!StringUtils.isEmpty(label.getLabelname())) { Predicate predicate = criteriaBuilder.like(root.get("labelname").as(String.class), "%" + label.getLabelname() + "%"); list.add(predicate); } if (!StringUtils.isEmpty(label.getState())) { Predicate predicate = criteriaBuilder.like(root.get("state").as(String.class), "%" + label.getState() + "%"); list.add(predicate); } if (!StringUtils.isEmpty(label.getRecommend())) { Predicate predicate = criteriaBuilder.like(root.get("recommend").as(String.class), "%" + label.getRecommend() + "%"); list.add(predicate); } if (!StringUtils.isEmpty(label.getId())) { Predicate predicate = criteriaBuilder.like(root.get("id").as(String.class), "%" + label.getId() + "%"); list.add(predicate); } return criteriaBuilder.and(list.toArray(new Predicate[0])); }); }
-
基于组合条件的分页查询:
-
分页查询只需要在方法中添加一个分页参数对象 Pageable,注意 jpa 中的页码是从 0 开始。
-
/** * 标签分页 * * @param label label * @param page page * @param size size * @return Page<Label> */ public Page<Label> findByLabelPage(Label label, int page, int size) { //jpa中的页码是从0开始,所以要减一。 return labelDao.findAll(getSpecification(label), PageRequest.of(page - 1, size)); }
-
-
-
自定义 Repository 方法。
- 条件首字母大写
- 列名首字母大写
- 注意顺序
-
复杂查询
-
对于多表的查询,比如多表的连接查询,另一种就是针对多表的子查询。实现方式主要是自己写 sql。
-
spring data jpa 写 sql 有两种方式:
-
面向对象的方式,写法称为 JPQL。
-
原生的 sql(推荐)。
-
package com.tensquare.qa.dao; import com.tensquare.qa.pojo.Problem; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; /** * @author 华韵流风 */ public interface ProblemDao extends JpaRepository<Problem, String>, JpaSpecificationExecutor<Problem> { /** * 最新问答 * * @param labelid labelid * @param pageable pageable * @return List<Problem> */ @Query(value = "select * from tb_problem p,tb_pl pl where p.id=pl.problemid and pl.labelid=?1 order by createtime desc", nativeQuery = true) List<Problem> findNewList(String labelid, Pageable pageable); /** * 热门问答 * * @param labelid labelid * @param pageable pageable * @return List<Problem> */ @Query(value = "select * from tb_problem p,tb_pl pl where p.id=pl.problemid and pl.labelid=:labelid order by reply desc", nativeQuery = true) List<Problem> findHotList(@Param("labelid") String labelid, Pageable pageable); /** * 等待问答 * * @param labelid labelid * @param pageable pageable * @return List<Problem> */ @Query(value = "select * from tb_problem p,tb_pl pl where p.id=pl.problemid and pl.labelid=:labelid and reply=0", nativeQuery = true) List<Problem> findWaitList(@Param("labelid") String labelid, Pageable pageable); }
-
-
-
四、使用 spring data redis 操作缓存
-
创建 redis 容器:docker run -di –-name=tensquare_redis -p 6379:6379 redis –restart=always。
-
引用启动器:
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
-
在配置文件中配置 redis 服务器:
-
server: port: 9005 spring: application: name: tensquare-article jpa: database: mysql show-sql: true datasource: username: root password: 123456 url: jdbc:mysql://192.168.46.130:3306/tensquare_article?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false&useUnicode=true&allowMultiQueries=true driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 5 minimum-idle: 2 idle-timeout: 2000 redis: host: 192.168.46.130 #port: 6379使用默认的端口可不写
-
-
使用:
-
package com.tensquare.article.service; import com.tensquare.article.dao.ArticleDao; import com.tensquare.article.pojo.Article; import com.tensquare.utils.IdWorker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author 华韵流风 * @ClassName ArticleService * @Date 2021/9/18 14:09 * @packageName com.tensquare.article.service * @Description TODO */ @Service @Transactional(rollbackFor = Exception.class) public class ArticleService { @Autowired private ArticleDao articleDao; @Autowired private IdWorker idWorker; /** * 注入redis模板,这里若使用@Autowired注解则需要使用 * 泛型的RedisTemplate。即不指定类型。 */ @Resource private RedisTemplate<String, Article> redisTemplate; /** * 添加article * * @param article article */ public void add(Article article) { article.setId(String.valueOf(idWorker.nextId())); articleDao.save(article); } /** * 根据id删除article * * @param id id */ public void remove(String id) { articleDao.deleteById(id); redisTemplate.delete("article_" + id); } /** * 根据id修改article * * @param id id * @param article article */ public void update(String id, Article article) { article.setId(id); articleDao.save(article); redisTemplate.delete("article_" + id); redisTemplate.opsForValue().set("article_" + id, findById(id), 24 * 60 * 60, TimeUnit.SECONDS); } /** * 根据id查询article * * @param id id * @return Article */ public Article findById(String id) { //从缓存中查找 Article article = redisTemplate.opsForValue().get("article_" + id); if (article == null) { article = articleDao.findById(id).get(); redisTemplate.opsForValue().set("article_" + id, article, 24 * 60 * 60, TimeUnit.SECONDS); } return article; } /** * 查询所有article * * @return List<Article> */ public List<Article> findAll() { return articleDao.findAll(); } }
-