gpt4 book ai didi

java - 停止 Tomcat 不会删除 Derby db.lck

转载 作者:塔克拉玛干 更新时间:2023-11-01 21:46:43 25 4
gpt4 key购买 nike

(编辑:我已经为这个问题增加了悬赏。我找到了一个解决方法(作为答案发布在下面),但我希望有人能够首先解释为什么需要这个解决方法。)

我有一个在开发期间连接到 Derby 数据库的 Spring webapp。这在我第一次运行 webapp 时工作正常,但在随后的运行中它在启动期间失败并出现“Derby 的另一个实例可能已经启动了数据库”SQLException。

我知道这是因为当我关闭 Tomcat 时与 Derby 的连接没有关闭,尽管我希望 Spring 自动处理它。所以我的问题是,如何正确断开与 Derby 的连接?不仅在手动停止 Tomcat 期间,而且在热部署新的 .war 文件期间?

我想避免使用 Derby 服务器,而且我还使用注释而不是 XML 配置。这是我原来的 PersistConfig 类:

package com.example.spring.config;

import java.beans.PropertyVetoException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.derby.jdbc.EmbeddedDataSource;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.jdbc.datasource.embedded.ConnectionProperties;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseConfigurer;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan({"com.example.spring.dao.jpa"})
@EnableTransactionManagement // <-- enable @Transactional annotations for spring @Component and stereotypes
public class PersistConfig{


@Bean
public HibernateExceptionTranslator exceptionTranslator() {
return new HibernateExceptionTranslator();
}

@Bean
public LocalSessionFactoryBean localSessionFactoryBean(DataSource dataSource, JpaVendorAdapter vendorAdapter) {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan("com.example.one", "com.example.two");

Properties properties = new Properties();
properties.putAll(vendorAdapter.getJpaPropertyMap());
localSessionFactoryBean.setHibernateProperties(properties);

return localSessionFactoryBean;
}

@Bean
public HibernateTransactionManager hibernateTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager(sessionFactory);

return hibernateTransactionManager;
}

@Configuration
public static class DevelopmentConfig{
@Bean
public DataSource dataSource() throws SQLException, PropertyVetoException {



DataSource dataSource = new SimpleDriverDataSource(new org.apache.derby.jdbc.EmbeddedDriver(), "jdbc:derby:C:\\Users\\Kevin\\Desktop\\DerbyDB", "", "");

System.out.println("RETURNING DATASOURCE");

return dataSource;

}

@Bean
JpaVendorAdapter vendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();

vendorAdapter.setDatabase(Database.DERBY);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.DerbyDialect");
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);

vendorAdapter.getJpaPropertyMap().put("hibernate.hbm2ddl.auto", "update");
vendorAdapter.getJpaPropertyMap().put("hbm2ddl.auto", "update");

return vendorAdapter;
}
}
}

我尝试使用 Runtime.addShutdownHook() 向整个 JVM 添加一个关闭 Hook ,我在其中手动断开与 Derby 数据库的连接,但这似乎从未被触发。

然后我被告知查看 EmbeddedDatabaseConfigurer 接口(interface)以添加一个 Spring 关闭回调,我在其中手动关闭数据库连接,这就是我想出的:

package com.example.spring.config;

import java.beans.PropertyVetoException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.derby.jdbc.EmbeddedDataSource;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.jdbc.datasource.embedded.ConnectionProperties;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseConfigurer;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan({"com.example.spring.dao.jpa"})
@EnableTransactionManagement // <-- enable @Transactional annotations for spring @Component and stereotypes
public class PersistConfig implements EmbeddedDatabaseConfigurer {


@Bean
public HibernateExceptionTranslator exceptionTranslator() {
return new HibernateExceptionTranslator();
}

@Bean
public LocalSessionFactoryBean localSessionFactoryBean(DataSource dataSource, JpaVendorAdapter vendorAdapter) {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan("com.example.one", "com.example.two");

Properties properties = new Properties();
properties.putAll(vendorAdapter.getJpaPropertyMap());
localSessionFactoryBean.setHibernateProperties(properties);

return localSessionFactoryBean;
}

@Bean
public HibernateTransactionManager hibernateTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager(sessionFactory);

return hibernateTransactionManager;
}

@Configuration
public static class DevelopmentConfig{
@Bean
public DataSource dataSource() throws SQLException, PropertyVetoException {



DataSource dataSource = new SimpleDriverDataSource(new org.apache.derby.jdbc.EmbeddedDriver(), "jdbc:derby:C:\\Users\\Kevin\\Desktop\\DerbyDB", "", "");

System.out.println("RETURNING DATASOURCE");

return dataSource;

}

@Bean
JpaVendorAdapter vendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();

vendorAdapter.setDatabase(Database.DERBY);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.DerbyDialect");
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);

vendorAdapter.getJpaPropertyMap().put("hibernate.hbm2ddl.auto", "update");
vendorAdapter.getJpaPropertyMap().put("hbm2ddl.auto", "update");

return vendorAdapter;
}
}

@Override
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {

System.out.println("CONFIGURE");

properties.setDriverClass(org.apache.derby.jdbc.EmbeddedDriver.class);
properties.setUrl("jdbc:derby:C:\\Users\\Kevin\\Desktop\\DerbyDB");
}

@Override
public void shutdown(DataSource ds, String databaseName) {

System.out.println("SHUTTING DOWN");

try {
DriverManager.getConnection("jdbc:derby:C:\\Users\\Kevin\\Desktop\\DerbyDB;shutdown=true");
}
catch (SQLException e) {
e.printStackTrace();
}
}

}

但是,似乎没有调用 configureConnectionProperties() 函数和 shutdown() 函数。我显然不知道自己在做什么,所以非常感谢任何指点。

最佳答案

编辑:添加重新启动嵌入式 Derby 数据库的精度和可能更简单的解决方案。

我至少可以部分重现问题,理解它并修复它。但我不能说为什么 BoneCP 工作正常。我只是注意到,如果我在关闭 tomcat 和重新启动它之间等待足够长的时间,它就会起作用。我想 BoneCP 不会立即访问数据库,而是等到第一次真正的连接。

首先是问题:当使用 Derby 作为嵌入式数据库时,数据库在第一次连接时启动,但必须明确关闭。如果不是,则不会删除 db.lock 文件,进一步的应用程序可能会再次启动数据库时遇到问题。 tomcat 或(默认情况下)spring 中不存在自动关闭此类数据库的方法。

接下来,为什么你尝试使用 EmbeddedDatabaseConfigurer 没有成功:EmbeddedDatabaseConfigurer 不是一个神奇的标记并且在类中继承它不足以让 spring 自动使用它。它只是一个接口(interface),必须由配置器实现以允许 EmbeddedDatabaseFactory 使用它。

终于修复了。您不应使用 SimpleDriverDataSource 从嵌入式 Derby 数据库获取连接,而应使用 EmbeddedDatabaseFactory。 Spring 默认知道 Derby 嵌入式数据库,你可以通过简单地设置类型来配置工厂......但这只适用于内存数据库并且你有一个文件数据库!本来就太简单了……你必须给工厂注入(inject)一个配置器才能让一切正常。

现在是代码(从您的第一个版本开始):

@Configuration
public static class DevelopmentConfig{
EmbeddedDatabaseFactory dsFactory;

public DevelopmentConfig() {
EmbeddedDatabaseConfigurer configurer = new EmbeddedDatabaseConfigurer() {
@Override
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
System.out.println("CONFIGURE");

properties.setDriverClass(org.apache.derby.jdbc.EmbeddedDriver.class);
properties.setUrl("jdbc:derby:C:\\Users\\Kevin\\Desktop\\DerbyDB");
}

@Override
public void shutdown(DataSource dataSource, String databaseName) {
final String SHUTDOWN_CODE = "XJ015";
System.out.println("SHUTTING DOWN");

try {
DriverManager.getConnection("jdbc:derby:;shutdown=true");
} catch (SQLException e) {
// Derby 10.9.1.0 shutdown raises a SQLException with code "XJ015"
if (!SHUTDOWN_CODE.equals(e.getSQLState())) {
e.printStackTrace();;
}
}
}
};
dsFactory = new EmbeddedDatabaseFactory();
dsFactory.setDatabaseConfigurer(configurer);
}

@Bean
public DataSource dataSource() throws SQLException, PropertyVetoException {

System.out.println("RETURNING DATASOURCE");

return dsFactory.getDatabase();
}

// remaining of code unchanged

这样,我就可以热重载war,当tomcat关闭时,db.lock正常销毁。

编辑:如果出现问题,Derby 文档建议添加以下命令以在关闭后重新启动数据库:Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();。它可能是 configureConnectionProperties 方法的最后一条指令。

但实际上,解决方案可能更简单。 真正需要添加到您的配置中的是正确关闭嵌入式驱动程序(并最终重新启动)。因此,一个简单的 PreDestroy(以及最终的 `@PostConstruct)注释方法就足够了:

@Configuration
public static class DevelopmentConfig{

@PreDestroy
public void shutdown() {
final String SHUTDOWN_CODE = "XJ015";
System.out.println("SHUTTING DOWN");

try {
DriverManager.getConnection("jdbc:derby:;shutdown=true");
} catch (SQLException e) {
// Derby 10.9.1.0 shutdown raises a SQLException with code "XJ015"
if (!SHUTDOWN_CODE.equals(e.getSQLState())) {
e.printStackTrace();
}
}
}

/* if needed ...
@PostConstruct
public void init() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
}
*/
@Bean
public DataSource dataSource() throws SQLException, PropertyVetoException {

DataSource dataSource = new SimpleDriverDataSource(new org.apache.derby.jdbc.EmbeddedDriver(), "jdbc:derby:C:\\Users\\Kevin\\Desktop\\DerbyDB", "", "");

System.out.println("RETURNING DATASOURCE");

return dataSource;

}

// remaining of code unchanged

此变体的主要优点是您可以选择数据源,从 SimpleDriverDataSource 到真实池。

关于java - 停止 Tomcat 不会删除 Derby db.lck,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24491328/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com