Spring4+Hibernate5+JPA+c3p0:SchemaCreator无法识别DataSource问题
问题描述
正在将现有应用升级为Spring 4 + Hibernate 5 + JPA + c3p0架构,遇到以下问题:
- SchemaCreator类无法从c3p0的DataSource bean中获取Hibernate连接信息
- 原代码通过注解显式传递dialect,不确定是否有必要这么做
- 当前报错:
Caused by: java.sql.SQLException: No suitable driver - 疑惑:已在entityManagerFactory中配置了DataSource引用,为何连接信息未被识别?同时想了解使用JPA时是否仍需使用Connection类。
相关代码
pom.xml版本配置
<spring.version>4.3.30.RELEASE</spring.version> <c3p0.version>0.9.5.5</c3p0.version> <hibernate.version>5.6.4.Final</hibernate.version> <hibernate-jpa-api.version>1.0.0.Final</hibernate-jpa-api.version> <hsqldb.version>2.7.0</hsqldb.version> .... <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>${c3p0.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <version>${hibernate-jpa-api.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-tools</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>${hibernate.version}</version> </dependency> ...
baseContext.xml配置
<bean id="propertyConfigurer" class="org.springframework.context.support.PortalPropertySourcesPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:datasource.properties</value> <value>classpath:configuration.properties</value> <value>file:${portal.home}/global.properties</value> <value>file:${portal.home}/simple-cms.properties</value> </list> </property> </bean> <bean id="propertyResolver" factory-bean="propertyConfigurer" factory-method="getPropertyResolver"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" p:driverClass="${hibernate.connection.driver_class}" p:jdbcUrl="${hibernate.connection.url}" p:user="${hibernate.connection.username}" p:password="${hibernate.connection.password}"> <property name="acquireIncrement" value="1"/> <property name="minPoolSize" value="1"/> <property name="initialPoolSize" value="1"/> <property name="maxPoolSize" value="5"/> <property name="maxIdleTime" value="900"/> <property name="testConnectionOnCheckout" value="true"/> <property name="contextClassLoaderSource" value="library"/> <property name="privilegeSpawnedThreads" value="true"/> <!-- End of c3p0 properties --> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> </props> </property> <property name="persistenceUnitName" value="AttachmentsDb" /> </bean> <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> <bean id="schemaCreator" class="org.jasig.portlet.attachment.util.SchemaCreator" /> ...
SchemaCreator类代码
public class SchemaCreator implements ApplicationContextAware { private static final String APPLICATION_CONTEXT_LOCATION = "classpath:/context/baseContext.xml"; private static final String DATA_SOURCE_BEAN_NAME = "dataSource"; private ApplicationContext applicationContext; private final Logger logger = LoggerFactory.getLogger(getClass()); <!-- Should I pass the other hibernate connection details like this?--> @Value("${hibernate.dialect}") private String hibernateDialect; public static void main(String[] args) { // Bootstrap an ApplicationContext... final GenericApplicationContext context = new GenericApplicationContext(); final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); final Resource resource = context.getResource(APPLICATION_CONTEXT_LOCATION); reader.loadBeanDefinitions(resource); context.refresh(); context.registerShutdownHook(); // close this context on JVM shutdown unless it has already been closed. final SchemaCreator schemaCreator = context.getBean("schemaCreator", SchemaCreator.class); System.exit(schemaCreator.create()); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } private int create() { final DataSource dataSource = applicationContext.getBean(DATA_SOURCE_BEAN_NAME, DataSource.class); try (final Connection conn = dataSource.getConnection()) { Map<String, String> settings = new HashMap<>(); settings.put("generateDdl", "false"); settings.put("hibernate.dialect",hibernateDialect); ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(settings).build(); MetadataSources metadata = new MetadataSources(serviceRegistry); metadata.addAnnotatedClass(Attachment.class); EnumSet<TargetType> enumSet = EnumSet.of(TargetType.DATABASE); SchemaExport schemaExport = new SchemaExport(); schemaExport.execute(enumSet, SchemaExport.Action.BOTH, metadata.buildMetadata()); final List<Exception> exceptions = schemaExport.getExceptions(); if (exceptions.size() != 0) { logger.error("Schema Create Failed; see below for details"); for (Exception e : exceptions) { logger.error("Exception from Hibernate Tools SchemaExport", e); } return 1; } } catch (SQLException sqle) { logger.error("Failed to initialize & invoke the SchemaExport tool", sqle); return 1; } return 0; } }
问题分析与解决方案
1. 解决"No suitable driver"错误
- 补充数据库驱动依赖:你的pom.xml中定义了
hsqldb.version但未添加hsqldb依赖,导致驱动类无法被加载。添加以下依赖:<dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> </dependency> - 校验jdbcUrl格式:HSQLDB的jdbcUrl需符合规范,比如内存库是
jdbc:hsqldb:mem:yourDbName,文件库是jdbc:hsqldb:file:/path/to/yourDb,确认datasource.properties中的hibernate.connection.url是否正确。 - 类加载验证:c3p0配置了
contextClassLoaderSource="library",确保驱动类能被应用类加载器正确加载,若仍有问题可尝试移除该配置。
2. SchemaCreator连接信息未被识别的原因及修复
问题出在SchemaCreator的create()方法中:你虽然获取了DataSource并拿到Connection,但未将DataSource或Connection传递给Hibernate的ServiceRegistry。Hibernate的SchemaExport默认会尝试新建连接,而它没有获取到你配置的数据源信息,因此触发驱动找不到的错误。
修复后的create()方法:
private int create() { final DataSource dataSource = applicationContext.getBean(DATA_SOURCE_BEAN_NAME, DataSource.class); try { Map<String, Object> settings = new HashMap<>(); settings.put(AvailableSettings.DIALECT, hibernateDialect); // 将已配置好的DataSource传给Hibernate,让它使用现有连接池 settings.put(AvailableSettings.DATASOURCE, dataSource); ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(settings).build(); MetadataSources metadata = new MetadataSources(serviceRegistry); metadata.addAnnotatedClass(Attachment.class); EnumSet<TargetType> enumSet = EnumSet.of(TargetType.DATABASE); SchemaExport schemaExport = new SchemaExport(); schemaExport.execute(enumSet, SchemaExport.Action.BOTH, metadata.buildMetadata()); final List<Exception> exceptions = schemaExport.getExceptions(); if (!exceptions.isEmpty()) { logger.error("Schema Create Failed; see below for details"); exceptions.forEach(e -> logger.error("Exception from Hibernate Tools SchemaExport", e)); return 1; } } catch (Exception e) { logger.error("Failed to initialize & invoke the SchemaExport tool", e); return 1; } return 0; }
关于显式传递dialect:Hibernate 5支持自动检测dialect,但显式配置更稳妥,尤其是对于HSQLDB这类多版本的数据库,能避免自动检测出错,因此保留当前的注解传递方式是合理的。
3. JPA是否需要使用Connection类
- 日常开发无需直接使用:常规JPA操作中,应通过
EntityManager进行数据库交互,JPA会自动管理连接、事务、实体映射等,无需手动操作Connection。 - 特殊场景可使用:像SchemaCreator这类需要执行DDL生成的工具场景,JPA标准没有提供原生支持,此时直接使用Connection或Hibernate原生API是合理的。
内容的提问来源于stack exchange,提问作者loulou
相关产品推荐
相关产品推荐

