【DB系列】JPA 错误姿势之环境配置问题

文章目录
  1. I. 配置问题
    1. 1. 基本配置
    2. 2. 注入失败case复现
    3. 3. case分析
    4. 4. 解决方案
    5. 5. 小结
  2. II. 其他
    1. 0. 项目
    2. 1. 一灰灰Blog

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

I. 配置问题

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

1. 基本配置

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

1
2
3
4
5
6
7
8
<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),指定数据库的配置信息

1
2
3
4
5
6
7
8
9
10
## 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库中,新增一个表

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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();
}
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
@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,并制定对应的包路径

1
2
3
4
5
@Configuration
@EnableJpaRepositories("com.git.hui.boot.jpacase")
@EntityScan("com.git.hui.boot.jpacase.entity")
public class TrueJpaCaseAutoConfiguration {
}

然后再次测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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或者有更好的建议,欢迎批评指正,不吝感激

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

一灰灰blog


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到