问题
We have a spring boot application.
When we do "mvn spring-boot:run", the application uses HikariCP. When we deploy a war file on tomcat, the CP is different and it crashes with connection closed after a few hours.
How can I force Hikari when deploying a war file?
This is our application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/xxx?
autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8_general_ci&characterSetResults=utf8&autoDeserialize=true&useConfigs=maxPerformance
spring.datasource.username=root
spring.datasource.password=___
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.open-in-view=true
spring.jpa.show-sql=false
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.DefaultNamingStrategy
spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
spring.jpa.properties.hibernate.hikari.dataSource.cachePrepStmts=true
spring.jpa.properties.hibernate.hikari.dataSource.prepStmtCacheSize=250
spring.jpa.properties.hibernate.hikari.dataSource.prepStmtCacheSqlLimit=2048
spring.jpa.properties.hibernate.hikari.dataSource.useServerPrepStmts=true
spring.jpa.properties.hibernate.generate_statistics=false
spring.jpa.properties.hibernate.cache.use_structured_entries=true
spring.jpa.properties.hibernate.archive.autodetection=class
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_update=true
spring.jpa.properties.hibernate.connection.characterEncoding=utf-8
spring.jpa.properties.hibernate.connection.CharSet=utf-8
spring.jpa.properties.hibernate.connection.useUnicode=true
spring.jpa.properties.hibernate.connection.autocommit=true
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory
spring.jpa.properties.hibernate.cache.region_prefix=
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.use_minimal_puts=true
# JTA
spring.jta.enabled=true
This is the Application class:
package site.app;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.Properties;
/**
* to make this deployable as war, this is necessary:
* http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html
*/
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = "site")
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = { "site.repository" } )
@EntityScan(basePackages="site.model")
@EnableJpaAuditing
@EnableSpringDataWebSupport
@SpringBootApplication//mist: so that it can be run as war file
public class Application extends SpringBootServletInitializer {
/** mist: so that it can be run as war file */
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// System.out.println("Let's inspect the beans provided by Spring Boot:");
//
// String[] beanNames = ctx.getBeanDefinitionNames();
// Arrays.sort(beanNames);
// for (String beanName : beanNames) {
// System.out.println(beanName);
// }
// UserRepository repository = context.getBean(UserRepository.class);
// //example data
// User user = new User();
// user.setEmail("nov34@test.com");
// user.setFirstName("test");
// user.setLastName("test");
//
// repository.save(user);
// Iterable<User> allUsers = repository.findAll();
// for(User theUser : allUsers){
// System.out.println(theUser.getEmail());
// }
//
// SponsorRepository sponsorRepository = context.getBean(SponsorRepository.class);
//
// Sponsor sponsor = new Sponsor();
// sponsor.setEmail("test@test.com");
// sponsor.setSponsorPackage(SponsorPackage.DIAMOND);
//
// sponsorRepository.save(sponsor);
// Page page = new Page();
// page.setName("home");
// PageRepository pageRepository = context.getBean(PageRepository.class);
// pageRepository.save(page);
// Link link = new Link();
// link.setName("Registration");
// link.setUrl("/");
// LinkRepository linkRepository = context.getBean(LinkRepository.class);
// linkRepository.save(link);
// context.close();
}
// private DataSource dataSource() {
// final HikariDataSource ds = new HikariDataSource();
// ds.setMaximumPoolSize(100);
// ds.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
// ds.addDataSourceProperty("url", url);
// ds.addDataSourceProperty("user", username);
// ds.addDataSourceProperty("password", password);
// ds.addDataSourceProperty("cachePrepStmts", true);
// ds.addDataSourceProperty("prepStmtCacheSize", 250);
// ds.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
// ds.addDataSourceProperty("useServerPrepStmts", true);
// return ds;
// }
// @Value("${spring.datasource.username}")
// private String user;
//
// @Value("${spring.datasource.password}")
// private String password;
//
// @Value("${spring.datasource.url}")
// private String dataSourceUrl;
//
// @Value("${spring.datasource.driverClassName}")
// private String driverClassName;
//
//// @Value("${spring.datasource.connectionTestQuery}")
//// private String connectionTestQuery;
//
// @Bean
// public DataSource primaryDataSource() {
// Properties dsProps = new Properties();
// dsProps.setProperty("url", dataSourceUrl);
// dsProps.setProperty("user", user);
// dsProps.setProperty("password", password);
//
// Properties configProps = new Properties();
//// configProps.setProperty("connectionTestQuery", connectionTestQuery);
// configProps.setProperty("driverClassName", driverClassName);
// configProps.setProperty("jdbcUrl", dataSourceUrl);
//
// HikariConfig hc = new HikariConfig(configProps);
// hc.setDataSourceProperties(dsProps);
//// hc.setMetricRegistry(metricRegistry);
// return new HikariDataSource(hc);
// }
}
This is the pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.bgjug</groupId>
<artifactId>site</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name> web site</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
<!--mist: exists in tomcat-->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--TODO mist: hikariCP stays here. I couldn't make it use it (I'm missing something). Now it uses either tomcat's pool or commons' pool-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<scope>compile</scope>
</dependency>
<!-- I need this to make entities auditable -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time-jsptags</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>${jadira.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<!--mist: exists in tomcat-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<!-- email -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-hibernate4</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.186</version>
</dependency>
</dependencies>
<!--mist: a profile that has the spring-boot:run plugin and a couple of dependencies, so that-->
<!--spring-boot:run will work with an embedded tomcat. This profile is activated by default so that-->
<!--no extra conf is needed. when we deploy on the server, we deactivate the profile, because we don't-->
<!--want these dependencies in the war.-->
<profiles>
<profile>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<id>run.as.spring-boot.run</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<properties>
<start-class>site.app.Application</start-class>
<spring.version>4.1.5.RELEASE</spring.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-data-jpa.version>1.5.0.M1</spring-data-jpa.version>
<hibernate-entitymanager.version>4.3.0.Final</hibernate-entitymanager.version>
<jadira.version>3.1.0.CR10</jadira.version>
<hazelcast.version>3.4</hazelcast.version>
<rest-assured.version>2.4.0</rest-assured.version>
<h2-database.version>1.3.156</h2-database.version>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
回答1:
So I've been told in the comments, that it's not going to work implicitly, because Tomcat's db pool somehow takes precedence, which is weird for me.
One can configure it explicitly like this:
@Configuration
@ConfigurationProperties//: so that the conf properties are supplied here
@SpringBootApplication//: so that it can be run as war file
...
public class Application extends SpringBootServletInitializer {
@Value("${spring.datasource.username}")
private String user;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.url}")
private String dataSourceUrl;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Bean
public DataSource primaryDataSource() {
Properties dsProps = new Properties();
dsProps.setProperty("url", dataSourceUrl);
dsProps.setProperty("user", user);
dsProps.setProperty("password", password);
Properties configProps = new Properties();
configProps.setProperty("driverClassName", driverClassName);
configProps.setProperty("jdbcUrl", dataSourceUrl);
HikariConfig hc = new HikariConfig(configProps);
hc.setDataSourceProperties(dsProps);
return new HikariDataSource(hc);
}
}
回答2:
I also faced this problem and in the end it was just a minor issue. It definitely takes precedence only in case you make sure tomcat-jdbc.jar or commons-dbcp.jar never show up in your classpath. you can ensure this by excluding these dependencies if they are transitively downloaded ( that is what i did, just find out what dependency in pom transitively downloads these in your classpath). when you are done with this your application.properties should look something like this:
# hikariCP
spring.jpa.databasePlatform=org.hibernate.dialect.MySQLDialect
spring.datasource.url=jdbc:mysql://localhost:3306/exampledb
spring.datasource.username=root
spring.datasource.password=
spring.datasource.poolName=SpringBootHikariCP
spring.datasource.maximumPoolSize=5
spring.datasource.minimumIdle=3
spring.datasource.maxLifetime=2000000
spring.datasource.connectionTimeout=30000
spring.datasource.idleTimeout=30000
spring.datasource.pool-prepared-statements=true
spring.datasource.max-open-prepared-statements=2500
it will implicitly configure a HikariCP datasource. But one thing more, it will use the Driver instead of dataSourceClassName which is recommended. In that case you have to go with the Java Config after having spring.datasource.dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource in application.properties:
@Configuration
@ComponentScan
class DataSourceConfig {
@Value("${spring.datasource.username}")
private String user;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.url}")
private String dataSourceUrl;
@Value("${spring.datasource.dataSourceClassName}")
private String dataSourceClassName;
@Value("${spring.datasource.poolName}")
private String poolName;
@Value("${spring.datasource.connectionTimeout}")
private int connectionTimeout;
@Value("${spring.datasource.maxLifetime}")
private int maxLifetime;
@Value("${spring.datasource.maximumPoolSize}")
private int maximumPoolSize;
@Value("${spring.datasource.minimumIdle}")
private int minimumIdle;
@Value("${spring.datasource.idleTimeout}")
private int idleTimeout;
@Bean
public DataSource primaryDataSource() {
Properties dsProps = new Properties();
dsProps.put("url", dataSourceUrl);
dsProps.put("user", user);
dsProps.put("password", password);
dsProps.put("prepStmtCacheSize",250);
dsProps.put("prepStmtCacheSqlLimit",2048);
dsProps.put("cachePrepStmts",Boolean.TRUE);
dsProps.put("useServerPrepStmts",Boolean.TRUE);
Properties configProps = new Properties();
configProps.put("dataSourceClassName", dataSourceClassName);
configProps.put("poolName",poolName);
configProps.put("maximumPoolSize",maximumPoolSize);
configProps.put("minimumIdle",minimumIdle);
configProps.put("minimumIdle",minimumIdle);
configProps.put("connectionTimeout", connectionTimeout);
configProps.put("idleTimeout", idleTimeout);
configProps.put("dataSourceProperties", dsProps);
HikariConfig hc = new HikariConfig(configProps);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
}
defining dataSourceClassName in application.properties for implicit auto-configuration with throw an exception :
Caused by: java.lang.IllegalStateException: both driverClassName and dataSourceClassName are specified, one or the other should be used.
As spring boot infers Driver from url property and you can't set Driver (inferred) and dataSourceClassName at the same time for implicit auto-configuration.
来源:https://stackoverflow.com/questions/29650501/hikaricp-starts-when-mvn-spring-bootrun-but-not-with-a-deployable-war-file