You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot整合Oracle需自定义方言及提供者吗?启动报错求助

问题描述

我正在做一个学校项目,需要开发一款基于Oracle数据库的Web应用。团队选用Spring Boot+Maven的MVC架构,由我负责后端开发。我基于JPA编写了Model、Repository、Service示例代码,还有application.properties配置和自定义DataSource类,但Spring应用启动即失败,报错信息如下:
Cannot invoke "org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(java.sql.SQLException, String)" because the return value of "org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.sqlExceptionHelper()" is null

我不确定是否需要自定义Oracle Dialect和DialectProvider,同时希望排查代码中的问题,学习规范的编码方式。


相关代码与配置

Model代码(Ceg.java)

package hu.projekt.webshop.Model;

import jakarta.persistence.*;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.mapping.Column;
import java.util.List;

@Entity
@Table(name = "CEG")
public class Ceg {

    @Id
    @Column("NEV")
    private String nev;
    @Column("ORSZAG")
    private String orszag;
    @Column("ALAPITVA")
    private Integer alapitva;
    @OneToMany(mappedBy = "gyarto")
    private List<Termek> termekek;

    public Ceg() {
    }

    public Ceg(String nev, String orszag, Integer alapitva) {
        this.nev = nev;
        this.orszag = orszag;
        this.alapitva = alapitva;
    }

    // Getters and Setters
    public String getNev() { return nev; }
    public void setNev(String nev) { this.nev = nev; }
    public String getOrszag() { return orszag; }
    public void setOrszag(String orszag) { this.orszag = orszag; }
    public Integer getAlapitva() { return alapitva; }
    public void setAlapitva(Integer alapitva) { this.alapitva = alapitva; }
    public List<Termek> getTermekek() { return termekek; }
    public void setTermekek(List<Termek> termekek) { this.termekek = termekek; }
}

Repository代码(CegRepository.java)

package hu.projekt.webshop.Repository;

import hu.projekt.webshop.Model.Ceg;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface CegRepository extends JpaRepository<Ceg, String> {

    @Override
    <S extends Ceg> S save(S entity);

    @Query("SELECT c FROM Ceg c WHERE c.nev = ?1")
    Ceg findByNev(String nev);

    @Override
    List<Ceg> findAll();

    @Query("DELETE FROM Ceg c WHERE c.nev = ?1")
    boolean deleteByNev(String nev);

}

Service代码(CegService.java)

package hu.projekt.webshop.Service;

import hu.projekt.webshop.Model.Ceg;
import hu.projekt.webshop.Repository.CegRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class CegService {

    private final CegRepository cegRepository;

    @Autowired
    public CegService(CegRepository cegRepository) {
        this.cegRepository = cegRepository;
    }

    public boolean cegMentes(String nev, String orszag, Integer alapitva) {
        return cegRepository.save(new Ceg(nev, orszag, alapitva)) != null;
    }

    public Ceg cegKereses(String nev) {
        return cegRepository.findByNev(nev);
    }

    public List<Ceg> cegLista() {
        return cegRepository.findAll();
    }

    public boolean cegTorles(String nev) {
        return cegRepository.deleteByNev(nev);
    }

}

application.properties配置

spring.application.name=Webshop

# Database connection properties
spring.datasource.url=jdbc:oracle:thin:@localhost:15210:orania2
spring.datasource.username=my_username
spring.datasource.password=my_password
oracle.ucp.minPoolSize=5
oracle.ucp.maxPoolSize=20
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver

# Hibernate properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

spring.main.allow-bean-definition-overriding=true

自定义DataSource类(DataSourceConfig.java)

package hu.projekt.webshop.Utilities;

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
public class DataSourceConfig {

    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${oracle.ucp.minPoolSize}")
    private String minPoolSize;
    @Value("${oracle.ucp.maxPoolSize}")
    private String maxPoolSize;
    @Value("${spring.datasource.driver-class-name:oracle.jdbc.pool.OracleDataSource}")
    private String driverClassName;

    @Bean(name = "OracleUniversalConnectionPool")
    @Primary
    public DataSource getDataSource() {
        PoolDataSource pds = null;
        try {
            pds = PoolDataSourceFactory.getPoolDataSource();

            pds.setConnectionFactoryClassName(driverClassName);
            pds.setURL(url);
            pds.setUser(username);
            pds.setPassword(password);
            pds.setMinPoolSize(Integer.parseInt(minPoolSize));
            pds.setInitialPoolSize(10);
            pds.setMaxPoolSize(Integer.parseInt(maxPoolSize));

        } catch (SQLException ea) {
            System.err.println("Error connecting to the database: " + ea.getMessage());
        }

        return pds;
    }

}

问题排查与规范建议

一、Model代码问题

  • 注解混用错误:同时使用了JPA的jakarta.persistence.*注解和Spring Data Relational的org.springframework.data.relational.core.mapping.Table/Column注解,两者分属不同框架不能混用。需统一替换为JPA注解:
    // 替换import
    import jakarta.persistence.Column;
    import jakarta.persistence.Table;
    
    // 注解写法调整
    @Entity
    @Table(name = "CEG")
    public class Ceg {
        @Id
        @Column(name = "NEV")
        private String nev;
        // 其余字段同理
    }
    
  • 主键设计风险:用字符串nev作为主键,存在名称重复或变更的隐患,建议改用自增数字主键配合Oracle序列:
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ceg_seq")
    @SequenceGenerator(name = "ceg_seq", sequenceName = "CEG_SEQ", allocationSize = 1)
    private Long id;
    
  • 关联关系完整性:@OneToMany(mappedBy = "gyarto")要求Termek实体中存在gyarto字段并标注@ManyToOne注解,否则会触发关联字段不存在的错误。

二、Repository代码问题

  • 删除冗余重写:save、findAll是JpaRepository自带方法,无需手动重写,直接删除这两个@Override方法。
  • 修复删除方法:JPQL的DELETE语句必须添加@Modifying和@Transactional注解,且返回值建议用int(影响行数)而非boolean:
    @Modifying
    @Transactional
    @Query("DELETE FROM Ceg c WHERE c.nev = ?1")
    int deleteByNev(String nev);
    
  • 简化查询方法:findByNev无需手动编写JPQL,Spring Data JPA支持方法名推导,直接定义Ceg findByNev(String nev);即可,框架会自动生成查询语句。

三、Service代码问题

  • 修正保存方法逻辑:save方法永远不会返回null,return cegRepository.save(...) != null的判断无意义,建议改为参数合法性校验或返回实体:
    public Ceg cegMentes(String nev, String orszag, Integer alapitva) {
        if (nev == null || nev.isBlank()) {
            throw new IllegalArgumentException("企业名称不能为空");
        }
        return cegRepository.save(new Ceg(nev, orszag, alapitva));
    }
    
  • 适配删除方法返回值:对应Repository的int返回值,Service中通过判断影响行数是否大于0返回boolean:
    public boolean cegTorles(String nev) {
        return cegRepository.deleteByNev(nev) > 0;
    }
    

四、配置与DataSource问题

  • 添加Oracle Dialect配置:Spring Boot 3.x后Hibernate不再自动检测Oracle Dialect,需手动在application.properties中添加:
    # Oracle 11g及以下用这个
    spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
    # Oracle 12c+用这个
    # spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
    
  • 删除自定义DataSource:Spring Boot已内置Oracle UCP支持,无需手动编写DataSourceConfig,直接通过配置启用UCP:
    spring.datasource.type=oracle.ucp.jdbc.PoolDataSource
    spring.datasource.oracleucp.min-pool-size=5
    spring.datasource.oracleucp.max-pool-size=20
    
  • 校验JDBC URL格式:Oracle 12c+推荐使用服务名格式URL:jdbc:oracle:thin:@localhost:15210/orania2,避免SID格式可能引发的连接问题。

五、启动报错根源解决

你遇到的SqlExceptionHelper空指针错误,主要由以下原因导致:

  1. Dialect未配置:Hibernate无法识别Oracle数据库,导致初始化失败,添加上述spring.jpa.database-platform配置即可解决。
  2. DataSource初始化返回null:自定义DataSourceConfig中捕获SQLException后返回null,导致Spring无法获取有效DataSource,删除自定义类改用自动配置即可避免。
  3. 依赖缺失:确认Maven依赖包含Oracle驱动、UCP和Spring Data JPA:
    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ojdbc11</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ucp</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    

内容的提问来源于stack exchange,提问作者KillerTiger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:29:52