公司岗位调整,我从一个安卓开发转后台开发,以前在其他公司也接触过后台,那时公司用的是ssm框架,不过安卓还是
主业务,后台业务也只是略而兼之,并未深研,想着公司这次给机会做后台,本着技多不压身,就欣然接受,此时已经2019年了,微服务方兴未艾,如火如荼,自然我也就去学习微服务了,在学习过程中,spring cloud使用JpaRepository,编译是能通过,在调试接口时报了一个空指针,经过debug调试,是接口
public interface ProductInfoRepository extends JpaRepository {
List findByProductStatus(List productIdStatus);
}
在下面的service中
@Service
public class ProductInfoServiceImpl implements IProductInfoService {
@Autowired(required = false)
private ProductInfoRepository productInfoRepository;
@Override
public List getProductList() {
List integerList = new ArrayList<>();
integerList.add("1");
List productInfoList = productInfoRepository.findByProductStatus(integerList);
return productInfoList;
}
}
其中private ProductInfoRepository productInfoRepository是null的,ProductInfoRepository没有注入到容器中
调试几遍还是报空指针,只好求助百度,可以说困扰了我4天,都没有找到spring cloud使用JpaRepository报空指针的解决方案
网上找到的以下方案,都试了,都无用
1.private ProductInfoRepositoryproductInfoRepository是不是没有用@Autowired(required = false)注解,检查自己写的代码,是注解了
2.public interface ProductInfoRepository加上@Component,@Repository, 加上后,调试结果,service中的productInfoRepository还是为null,继续报空指针
3.controller和service中的方法是不是private的,检查自己的代码,方法都是public,排除该怀疑对象
4.entity实体类加上@table,@colum,调试结果,service中的productInfoRepository还是为null,报空指针
5.甚至自己怀疑是不是(required = false)了,去掉该配置,自然就报了另外一个异常,方案还是不对
6.Application上加上
@ComponentScan(basePackages = {"com.chengfeng.product.controller","com.chengfeng.product.service"}),
@EntityScan(basePackages = "com.chengfeng.product.entity")
@EnableJpaRepositories(basePackages = "com.chengfeng.product.repository")
即代码为
@SpringBootApplication()(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@ComponentScan(basePackages = {"com.chengfeng.product.controller","com.chengfeng.product.service"}),
@EntityScan(basePackages = "com.chengfeng.product.entity")
@EnableJpaRepositories(basePackages = "com.chengfeng.product.repository")
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
上述加上,调试结果,service中的productInfoRepository还是为null,报空指针,
7.检查定义的entity实体类ProductInfo中的属性字段是否与数据库中一致,一一检查,都一一对应,排除该怀疑对象
8.当然什么重启了,清除缓存,说jar包冲突的,把原来的jar包全部清除重新下载,依然无用。
说实话,搞了几天都快崩溃了,放弃心又不甘,元气恢复,又鬼使神差继续调试。
9.是不是new service对象或者new ProductInfoRepository对象了,检查自己的代码,也排除该怀疑对象
10.最后实在没有脾气了,把ProductInfoRepository定义的接口屏蔽掉,用JpaRepository原生生成的默认方法,还是报空指针。
最后,看了一眼ProductApplication
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
死马当活马医,把(exclude = DataSourceAutoConfiguration.class)去掉,将原来出现的
Error creating bean with name 'entityManagerFactory' defined in class path resource
[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]
异常暴露出来,把这个问题解决,看能不能顺便把空指针解决,然而运行后先报
HHH000342: Could not obtain connection to query metadata : Driver com.mysql.cj.jdbc.Driver claims to not accept jdbcUrl,
jdbc:mysql:http://localhost:3306/product?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
了一个新的问题,再报
Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'entityManagerFactory' defined in class path resource
[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]:
Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException:
Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
根据网上资料,application.properties中的spring.datasource.***肯定有问题,仔细看了几遍application.properties:
spring.application.name=product
server.port=8081
eureka.client.service-url.defaultZone: http://localhost:8761/eureka/
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql:http://localhost:3306/product?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = 123456
spring.jpa.hibernate.ddl-auto = update
# 方便jpa调试的配置
spring.jpa.show-sql = true
也没发现什么问题,最后经过对比,发现jdbc:mysql后面多了个http,之前没有开发配置过数据库,也不知这里到底如何配置,什么时候这里多了个http,难道是因为多了个http,抱着疑问的态度改成
spring.datasource.url= jdbc:mysql://localhost:3306/product?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
奇迹出现了,项目成功运行起来,最后接口调试,service中的productInfoRepository也成功注入容器了了,接口也调通了,也不报空指针了。此刻我已经知道,肯定是ProductInfoRepository中的接口写的不符合jpa规范,所以才造成ProductInfoRepository不能成功注入容器,将原来的方法:
// List findByProductStatus(List productIdStatus);
放开,运行,果然是又报错,
@SpringBootApplication加上(exclude = DataSourceAutoConfiguration.class),运行项目,错误消失,接口调试,继续报空指针,
至于为什么这个方法造成jpa不能注入容器,想必是这种接口定义方法不符合jpa规范,jpa识别不了。
最终,spring cloud使用jpa,要避免Repository不能注入容器,防止空指针的发生,除了排除上述10点怀疑对象,还要注意以下3点:
(1)@SpringBootApplication不能加(exclude = DataSourceAutoConfiguration.class)
(2)application.properties中关于数据库的配置一定要准确
(3)继承JpaRepository的dao层接口中自己定义的方法一定要符合jpa规范,按jpa的提示定义相关防范,不要自己随意命名,
最后附上spring cloud demo的部分
pom.xml代码
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.1.RELEASE
com.chengfeng
product
0.0.1-SNAPSHOT
product
Demo project for Spring Boot
UTF-8
UTF-8
1.8
Hoxton.RC2
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-test
com.fasterxml.jackson.core
jackson-annotations
org.springframework.boot
spring-boot-starter-actuator
junit
junit
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
application.properties代码:
spring.application.name=product
server.port=8081
eureka.client.service-url.defaultZone: http://localhost:8761/eureka/
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://localhost:3306/product?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = 123456
spring.jpa.hibernate.ddl-auto = update
# 方便jpa调试的配置
spring.jpa.show-sql = true
package com.chengfeng.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
controller层ProductController.java代码
package com.chengfeng.product.controller;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private IProductInfoService productInfoService;
@GetMapping("/list")
public void getProductList() {
System.out.println("开始获取getProductList");
// 1.查询所有商品
List productInfoList = productInfoService.getProductList();
System.out.println("获取的getProductList == ",productInfoList);
}
}
Entity实体类ProductInfo.java代码
package com.chengfeng.product.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.math.BigDecimal;
import java.util.Date;
/**
* 这里表名会自动转换,数据库中驼峰会是下划线,下面的变量也是
*/
@Entity
public class ProductInfo {
@Id
private String productId;
/** 名字. */
private String productName;
/** 单价. */
private BigDecimal productPrice;
/** 库存. */
private Integer productStock;
/** 描述. */
private String productDescription;
/** 小图. */
private String productIcon;
/** 状态, 0正常1下架. */
private Integer productStatus;
/** 类目编号. */
private Integer categoryType;
private Date createTime;
private Date updateTime;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public BigDecimal getProductPrice() {
return productPrice;
}
public void setProductPrice(BigDecimal productPrice) {
this.productPrice = productPrice;
}
public Integer getProductStock() {
return productStock;
}
public void setProductStock(Integer productStock) {
this.productStock = productStock;
}
public String getProductDescription() {
return productDescription;
}
public void setProductDescription(String productDescription) {
this.productDescription = productDescription;
}
public String getProductIcon() {
return productIcon;
}
public void setProductIcon(String productIcon) {
this.productIcon = productIcon;
}
public Integer getProductStatus() {
return productStatus;
}
public void setProductStatus(Integer productStatus) {
this.productStatus = productStatus;
}
public Integer getCategoryType() {
return categoryType;
}
public void setCategoryType(Integer categoryType) {
this.categoryType = categoryType;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
dao层ProductInfoRepository.java代码
package com.chengfeng.product.repository;
import com.chengfeng.product.entity.ProductInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProductInfoRepository extends JpaRepository {
List findByProductStatus(Integer productStatus);
}
服务层ProductInfoServiceImpl.java代码
package com.chengfeng.product.service;
import com.chengfeng.product.common.ProductStatusEnum;
import com.chengfeng.product.entity.ProductInfo;
import com.chengfeng.product.repository.ProductInfoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductInfoServiceImpl implements IProductInfoService {
@Autowired(required = false)
private ProductInfoRepository productInfoRepository;
@Override
public List getProductList() {
List productInfoList = productInfoRepository.findByProductStatus(ProductStatusEnum.UP.getCode());
return productInfoList;
}
}