191218-SpringBoot 系列教程 JPA 错误姿势之环境配置问题

又回到 jpa 的教程上了,这一篇源于某个简单的项目需要读写 db,本想着直接使用 jpa 会比较简单,然而悲催的是实际开发过程中,发现了不少的坑;本文为错误姿势第一篇,Repository 接口无法注入问题

I. 配置问题

新开一个 jpa 项目结合 springboot 可以很方便的实现,但是在某些环境下,可能会遇到自定义的 JpaRepository 接口无法注入问题

1. 基本配置

在 spring-boot 环境中,需要在pom.xml文件中,指定下面两个依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

接下来需要修改一下配置文件(application.properties),指定数据库的配置信息

## DataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password= spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

2. 注入失败 case 复现

首先在 mysql 的 story 库中,新增一个表

CREATE TABLE `meta_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group` varchar(32) NOT NULL DEFAULT '' COMMENT '分组',
`profile` varchar(32) NOT NULL DEFAULT '' COMMENT 'profile 目前用在应用环境 取值 dev/test/pro',
`desc` varchar(64) NOT NULL DEFAULT '' COMMENT '解释说明',
`deleted` int(4) NOT NULL DEFAULT '0' COMMENT '0表示有效 1表示无效',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `group_profile` (`group`,`profile`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='业务配置分组表';

然后定义这个表对应的 Entity

@Data
@Entity
@Table(name = "meta_group")
public class MetaGroupPO {
@Id
@Column(name = "`id`")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; @Column(name = "`group`")
private String group; @Column(name = "`profile`")
private String profile; @Column(name = "`desc`")
private String desc; @Column(name = "`deleted`")
private Integer deleted; @Column(name = "`create_time`")
@CreatedDate
private Timestamp createTime; @Column(name = "`update_time`")
@CreatedDate
private Timestamp updateTime;
}

对应的 repository 接口

public interface GroupJPARepository extends JpaRepository<MetaGroupPO, Integer> {

    List<MetaGroupPO> findByProfile(String profile);

    MetaGroupPO findByGroupAndProfileAndDeleted(String group, String profile, Integer deleted);

    @Modifying
@Query("update MetaGroupJpaPO m set m.desc=?2 where m.id=?1")
int updateDesc(int groupId, String desc); @Modifying
@Query("update MetaGroupJpaPO m set m.deleted=1 where m.id=?1")
int logicDeleted(int groupId);
}

一个简单的数据操作封装类GroupManager

@Component

public class GroupManager {
@Autowired
private GroupJPARepository groupJPARepository; public MetaGroupPO getOnlineGroup(String group, String profile) {
return groupJPARepository.findByGroupAndProfileAndDeleted(group, profile, 0);
} public Integer addGroup(String group, String profile, String desc) {
MetaGroupPO jpa = new MetaGroupPO();
jpa.setGroup(group);
jpa.setDesc(desc);
jpa.setProfile(profile);
jpa.setDeleted(0);
Timestamp timestamp = Timestamp.from(Instant.now());
jpa.setCreateTime(timestamp);
jpa.setUpdateTime(timestamp);
MetaGroupPO res = groupJPARepository.save(jpa);
return res.getId();
}
}

接下来重点来了,当我们的启动类,不是在外面时,可能会出现问题;项目结构如下

我们看一下配置类,和错误的启动应用类

@Configuration
@ComponentScan("com.git.hui.boot.jpacase")
public class JpaCaseAutoConfiguration {
} @SpringBootApplication
public class ErrorApplication { public static void main(String[] args) {
SpringApplication.run(ErrorApplication.class);
} }

直接启动失败,异常如下图,提示找不到GroupJPARepository这个 bean,而这个 bean 在正常启动方式中,会由 spring 帮我们生成一个代理类;而这里显然是没有生成了

3. case 分析

上面的 case 可能有点极端了,一般来讲项目启动类,我们都会放在最外层;基本上不太会出现上面这种项目结构,那么分析这个 case 有毛用?

一个典型的 case

  • 我们将 db 操作的逻辑放在一个 module(如 dao.jar)中封装起来
  • 然后有一个启动的 module,通过 maven 引入上 dao.jar
  • 这是入口的默认扫描范围,可能就无法包含 dao.jar,因此极有可能导致注入失败

4. 解决方案

那么该怎么解决这个问题呢?

在配置类中,添加两个注解EnableJpaRepositoriesEntityScan,并制定对应的包路径

@Configuration
@EnableJpaRepositories("com.git.hui.boot.jpacase")
@EntityScan("com.git.hui.boot.jpacase.entity")
public class TrueJpaCaseAutoConfiguration {
}

然后再次测试

@SpringBootApplication
public class TrueApplication { public TrueApplication(GroupManager groupManager) {
int groupId = groupManager.addGroup("true-group", "dev", "正确写入!!!");
System.out.println("add groupId: " + groupId);
MetaGroupPO po = groupManager.getOnlineGroup("true-group", "dev");
System.out.println(po);
} public static void main(String[] args) {
SpringApplication.run(ErrorApplication.class);
}
}

5. 小结

最后小结一下,当我们发现 jpa 方式的 Repository 无法注入时,一般是因为接口不再我们的扫描路径下,需要通过@EntityScan@EnableJpaRepositories来额外指定

(因为篇幅问题,其他的问题拆分到其他的博文)

II. 其他

0. 项目

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

SpringBoot 系列教程 JPA 错误姿势之环境配置问题的更多相关文章

  1. SpringBoot系列教程JPA之新增记录使用姿势

    SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第 ...

  2. SpringBoot系列教程JPA之query使用姿势详解之基础篇

    前面的几篇文章分别介绍了CURD中的增删改,接下来进入最最常见的查询篇,看一下使用jpa进行db的记录查询时,可以怎么玩 本篇将介绍一些基础的查询使用姿势,主要包括根据字段查询,and/or/in/l ...

  3. SpringBoot系列教程JPA之delete使用姿势详解

    原文: 190702-SpringBoot系列教程JPA之delete使用姿势详解 常见db中的四个操作curd,前面的几篇博文分别介绍了insert,update,接下来我们看下delete的使用姿 ...

  4. SpringBoot系列教程JPA之update使用姿势

    原文: 190623-SpringBoot系列教程JPA之update使用姿势 上面两篇博文拉开了jpa使用姿势的面纱一角,接下来我们继续往下扯,数据插入db之后,并不是说就一层不变了,就好比我在银行 ...

  5. SpringBoot系列教程JPA之指定id保存

    原文链接: 191119-SpringBoot系列教程JPA之指定id保存 前几天有位小伙伴问了一个很有意思的问题,使用 JPA 保存数据时,即便我指定了主键 id,但是新插入的数据主键却是 mysq ...

  6. SpringBoot系列教程web篇之Beetl环境搭建

    前面两篇分别介绍了目前流行的模板引擎Freemaker和Thymeleaf构建web应用的方式,接下来我们看一下号称性能最好的国产模板引擎Beetl,如何搭建web环境 本文主要来自官方文档,如有疑问 ...

  7. SpringBoot系列教程web篇之Thymeleaf环境搭建

    上一篇博文介绍了如何使用Freemaker引擎搭建web项目,这一篇我们则看一下另外一个常见的页面渲染引擎Thymeleaf如何搭建一个web项目 推荐结合Freemaker博文一起查看,效果更佳 1 ...

  8. SpringBoot系列教程web篇之Freemaker环境搭建

    现在的开发现状比较流行前后端分离,使用springboot搭建一个提供rest接口的后端服务特别简单,引入spring-boot-starter-web依赖即可.那么在不分离的场景下,比如要开发一个后 ...

  9. SpringBoot系列教程JPA之基础环境搭建

    JPA(Java Persistence API)Java持久化API,是 Java 持久化的标准规范,Hibernate是持久化规范的技术实现,而Spring Data JPA是在 Hibernat ...

随机推荐

  1. 一文搞定十大经典排序算法(Java实现)

    本文总结十大经典排序算法及变形,并提供Java实现. 参考文章: 十大经典排序算法总结(Java语言实现) 快速排序算法—左右指针法,挖坑法,前后指针法,递归和非递归 快速排序及优化(三路划分等) 一 ...

  2. Java集合Collection基本方法

    jdk1.7 api中的方法摘要: 参考java集合大全图:https://www.cnblogs.com/xkzhangsanx/p/10889114.html Collection为List.Se ...

  3. Zifencei扩展

    fence 指令对外部可见的访存请求,如设备 I / O 访问,内存访问等进行串行化.外部可见是指对处理器的其他核心.线程,外部设备或协处理器可见. fence.i 指令同步指令和数据流.在执行 fe ...

  4. Lua 字符串查找函数 string.find(s, pattern [, init [, plain]] )【转】

    函数原型 string.find(s, pattern [, init [, plain]] ) s: 源字符串 pattern: 待搜索模式串 init: 可选, 起始位置 plain: 我没用过 ...

  5. spoon数据转换中文乱码(kettle)

    (1) 查看mysql数据库是否为utf8(status) (2) 设置spoon (3) 文本打开spoon.bat,找到set OPT=%OPT% %PENTAHO_DI_JAVA_OPTIONS ...

  6. Linux服务管理之ntp

    NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议. 在计算机的世界里,时间非常地重要,例如对于火箭发射这种科研活动,对时间的统一性和准确性要 ...

  7. 微信小程序,相关代码

    table th:nth-of-type(1) { width: 200px; } 代码 作用 bindtap bindload 当图片载入完毕时触发,event.detail = {height, ...

  8. mysql之drop、truncate和delete的区别

    今天在整理mysql数据库笔记的时候突然想到一个问题,就是drop.truncate和delete的区别,乍一看三者都是有删除的功能,但是具体来看还是有很多区别的.我先把这三个的作用简单说一下,有前辈 ...

  9. MacOS更改启动台(launchpad)图标大小

    原文链接:https://www.jianshu.com/p/18224d191bd2作者:麦壳君 小麦有点强迫症,觉得 Mac 启动台(Launchpad)里面系统默认的应用程序图标太大,所以想让它 ...

  10. Pandas | 05 基本功能

    到目前为止,我们了解了三种Pandas数据结构以及如何创建它们.接下来将主要关注数据帧(DataFrame)对象,因为它在实时数据处理中非常重要,并且还讨论其他数据结构. 一.系列基本功能 编号 属性 ...