DataSource实例化失败:Spring Boot连接MSSQL的JPA配置问题
问题概述
连接外部MSSQL数据库时触发Bean实例化空指针异常,已尝试添加@EnableJpaRepositories注解、排除DataSourceAutoConfiguration,MyBatis可正常连接但业务要求使用JPA。相关配置及异常堆栈如下:
配置文件(application.yml)
ansir.datasource: schema: schema jdbc-url: jdbc:sqlserver://SERVER:PORTNO;databaseName=DB;encrypt=false username: root password: pwd driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver hikari.maximum-pool-size: 10 hikari.pool-name: HikariPool-ANSIR jpa: show-sql: true hibernate.dialect: org.hibernate.dialect.SQLServer2012Dialect hibernate.ddl-auto: none
自定义配置类(ANSIRConfiguration.java)
@Value("${ansir.datasource}") private String SCHEMA_NAME; @Value("${ansir.datasource.jdbc-url}") private String jdbcUrl; @Value("${ansir.datasource.username}") private String username; @Value("${ansir.datasource.password}") private String password; @Value("${ansir.datasource.driver-class-name}") private String driverClassName; @Value("${ansir.datasource.hikari.maximum-pool-size}") private String maxPool; @Value("${ansir.datasource.hikari.pool-name}") private String poolName; @Value("${ansir.datasource.jpa.show-sql}") private String showSql; @Value("${ansir.datasource.jpa.hibernate.dialect}") private String hibernateDialect; @Value("${ansir.datasource.jpa.hibernate.ddl-auto}") private String ddl; private final Environment env; @Autowired public ANSIRConfiguration(Environment env) { this.env = env; } @Bean @ConfigurationProperties("ansir.datasource") public DataSource ansirDataSource() { return DataSourceBuilder.create() .driverClassName(env.getProperty(driverClassName)) .url(env.getProperty(jdbcUrl)) .username(env.getProperty(username)) .password(env.getProperty(password)) .build(); } @Bean public PlatformTransactionManager ansirTransactionManager(@Qualifier("ansirEntityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( entityManagerFactoryBean.getObject()); return transactionManager; } @Bean("ansirEntityManager") public LocalContainerEntityManagerFactoryBean ansirEntityManager(@Qualifier("ansirDataSource")DataSource ansirDataSource) { LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); entityManagerFactory.setDataSource(ansirDataSource); entityManagerFactory.setPackagesToScan("com.autonation.ca.client.ansir.entity"); entityManagerFactory.setPersistenceUnitName("ansir"); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); entityManagerFactory.setJpaVendorAdapter(vendorAdapter); HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty(ddl)); properties.put("hibernate.dialect", env.getProperty(hibernateDialect)); properties.put("hibernate.proc.param_null_passing", true); properties.put("hibernate.show_sq", showSql); entityManagerFactory.setJpaPropertyMap(properties); return entityManagerFactory; }
异常堆栈
Caused by: java.lang.NullPointerException: null at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) at java.base/java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1541) at java.base/java.lang.ClassLoader.getClassLoadingLock(ClassLoader.java:667) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:591) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) at com.zaxxer.hikari.HikariConfig.attemptFromContextLoader(HikariConfig.java:970) at com.zaxxer.hikari.HikariConfig.setDriverClassName(HikariConfig.java:480) at org.springframework.boot.jdbc.DataSourceBuilder$MappedDataSourceProperty.set(DataSourceBuilder.java:460) at org.springframework.boot.jdbc.DataSourceBuilder$MappedDataSourceProperties.set(DataSourceBuilder.java:355) at org.springframework.boot.jdbc.DataSourceBuilder.build(DataSourceBuilder.java:190) at com.autonation.ca.client.ansir.config.ANSIRConfiguration.ansirDataSource(ANSIRConfiguration.java:64) ...
问题根源及修复方案
从堆栈分析,空指针出现在HikariCP加载驱动类时,核心问题集中在配置类的属性注入与DataSource构建逻辑错误:
1. 数据源构建逻辑错误
ansirDataSource()方法中,错误地使用env.getProperty(driverClassName)——driverClassName已经是通过@Value注入的驱动类全名(如com.microsoft.sqlserver.jdbc.SQLServerDriver),而非配置文件的key(ansir.datasource.driver-class-name),调用env.getProperty会返回null,导致Hikari无法加载驱动类。
修复:
直接使用已注入的属性值,无需再通过env.getProperty获取:
@Bean // 移除@ConfigurationProperties,避免与手动配置冲突,或完全依赖自动绑定 public DataSource ansirDataSource() { return DataSourceBuilder.create() .driverClassName(driverClassName) .url(jdbcUrl) .username(username) .password(password) .build(); }
若要使用@ConfigurationProperties自动绑定Hikari配置,可简化为:
@Bean @ConfigurationProperties("ansir.datasource") public DataSource ansirDataSource() { return DataSourceBuilder.create().build(); }
此时无需手动设置url、username等属性,Spring会自动绑定配置文件中ansir.datasource下的所有属性。
2. @Value注入错误
@Value("${ansir.datasource}")试图注入整个数据源对象,而非指定的schema属性,应改为:
@Value("${ansir.datasource.schema}") private String SCHEMA_NAME;
3. JPA属性拼写错误
EntityManager配置中hibernate.show_sq拼写错误,应为hibernate.show_sql,否则SQL日志配置不生效:
properties.put("hibernate.show_sql", showSql);
4. 事务管理器初始化风险
直接调用entityManagerFactoryBean.getObject()可能导致EntityManagerFactory未完成初始化,应改为注入EntityManagerFactory:
@Bean public PlatformTransactionManager ansirTransactionManager(@Qualifier("ansirEntityManager") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); }
5. JPA属性获取冗余
已通过@Value注入ddl、hibernateDialect等属性,无需再调用env.getProperty,直接使用注入值即可:
properties.put("hibernate.hbm2ddl.auto", ddl); properties.put("hibernate.dialect", hibernateDialect);
最终修复后的配置类示例
@Configuration @EnableJpaRepositories(basePackages = "com.autonation.ca.client.ansir.repository", entityManagerFactoryRef = "ansirEntityManager", transactionManagerRef = "ansirTransactionManager") public class ANSIRConfiguration { @Value("${ansir.datasource.schema}") private String SCHEMA_NAME; @Value("${ansir.datasource.jdbc-url}") private String jdbcUrl; @Value("${ansir.datasource.username}") private String username; @Value("${ansir.datasource.password}") private String password; @Value("${ansir.datasource.driver-class-name}") private String driverClassName; @Value("${ansir.datasource.jpa.show-sql}") private String showSql; @Value("${ansir.datasource.jpa.hibernate.dialect}") private String hibernateDialect; @Value("${ansir.datasource.jpa.hibernate.ddl-auto}") private String ddl; @Bean public DataSource ansirDataSource() { return DataSourceBuilder.create() .driverClassName(driverClassName) .url(jdbcUrl) .username(username) .password(password) .build(); } @Bean public PlatformTransactionManager ansirTransactionManager(@Qualifier("ansirEntityManager") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); } @Bean("ansirEntityManager") public LocalContainerEntityManagerFactoryBean ansirEntityManager(@Qualifier("ansirDataSource") DataSource ansirDataSource) { LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); entityManagerFactory.setDataSource(ansirDataSource); entityManagerFactory.setPackagesToScan("com.autonation.ca.client.ansir.entity"); entityManagerFactory.setPersistenceUnitName("ansir"); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); entityManagerFactory.setJpaVendorAdapter(vendorAdapter); HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", ddl); properties.put("hibernate.dialect", hibernateDialect); properties.put("hibernate.proc.param_null_passing", true); properties.put("hibernate.show_sql", showSql); entityManagerFactory.setJpaPropertyMap(properties); return entityManagerFactory; } }
额外注意事项
- 确保主启动类排除默认数据源自动配置:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) - 确认MSSQL驱动依赖已正确引入(如
com.microsoft.sqlserver:mssql-jdbc) - 若使用多数据源,需确保
@EnableJpaRepositories指定对应的repository包、entityManagerFactory和transactionManager引用
内容的提问来源于stack exchange,提问作者clement mensah

