问题
Am maintaining a codebase which was written in Spring MVC 4.3.9.RELEASE (not Spring Boot)...
Under src/main/resources:
There are two different database configuration files:
sampledb.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Initialization for data source dbcp -->
<bean id="sampleDatabase" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql://localhost/sampledb?zeroDateTimeBehavior=convertToNull</value></property>
<property name="username"><value>root/value></property>
<property name="password"><value></value></property>
<property name="maxIdle" value="10"/>
<property name="maxActive" value="50"/>
<property name="maxWait" value="100"/>
<property name="defaultAutoCommit" value="false"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="1"/>
<property name="minIdle" value="0"></property>
<property name="timeBetweenEvictionRunsMillis" value="1000"></property>
<property name="minEvictableIdleTimeMillis" value="1000"></property>
</bean>
</beans>
eventsdb.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Initialization for data source dbcp -->
<bean id="eventsDatabase" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql://localhost/eventsdb?zeroDateTimeBehavior=convertToNull</value></property>
<property name="username"><value>root</value></property>
<property name="password"><value></value></property>
<property name="maxIdle" value="10"/>
<property name="maxActive" value="50"/>
<property name="maxWait" value="100"/>
<property name="defaultAutoCommit" value="false"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="1"/>
<property name="minIdle" value="0"></property>
<property name="timeBetweenEvictionRunsMillis" value="1000"></property>
<property name="minEvictableIdleTimeMillis" value="1000"></property>
</bean>
</beans>
WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>MyApp</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
WEB-INF/mvc-dispatcher-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<context:component-scan base-package="com.myapp.rest.controllers" />
<mvc:annotation-driven />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
Sample webservice code:
package com.myapp.rest.controllers;
@Controller
@RequestMapping("/v2")
public class MyController {
@RequestMapping(value="users/{userId}",method=RequestMethod.GET)
public @ResponseBody Object getUserDetails(@PathVariable String userId){
Object response=null;
UserDAO dao = UserDAO.getInstance();
response=dao.getUser(userId);
return response;
}
}
UserDao:
public class UserDAO {
private static UserDAO instance = null;
private JdbcTemplate jdbcTemplateObject = null;
public static UserDAO getInstance() {
if(instance == null) {
synchronized(UserDAO.class) {
if(instance == null) {
instance = new UserDAO();
}
}
}
return instance ;
}
UserDAO() {
try {
initializeDB();
}
catch(Exception e) {
e.printStackTrace();
}
}
private void initializeDB() {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("sampledb.xml");
DataSource dataSource = (DataSource) context.getBean("sampleDatabase");
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
catch (Exception e) {
e.printStackTrace();
}
}
// others methods which do the actual queries using Spring JDBC
}
The previous author has used this pattern (initializing the DB using ApplicationContext) in every single DAO (there's like 20 different ones in the codebase each doing the same thing with the same two database config files)!
Question(s):
This seems very inadequate (seems like should be done once), how can this (loading Spring based DB config files) be done once as soon as the war file loadings into Tomcat?
What's the best techniques for performance gains (e.g. should I use a caching system or a database connection pool)?
Any advice is greatly appreciated...
回答1:
private void initializeDB() {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("sampledb.xml");
DataSource dataSource = (DataSource) context.getBean("sampleDatabase");
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
catch (Exception e) {
e.printStackTrace();
}
}
This code is very dangerous, depending on the size of your context, you eventually will run into issues. What happens here is you are loading the whole application each time you need an object, you will open up connections to the db (which eventually will stop working due to too many connections) you will have weird transaction issues and probably (depending on the size) memory issues. (Of course if that is what you want by all means proceed like this).
Instead you should be using dependency injection. Declare all needed dependencies as fields and let spring do the auto wiring, which will happen just once at startup.
@Controller
@RequestMapping("/v2")
public class MyController {
private final UserDAO dao;
@Autowired
public MyController(UserDAO Dao) {
this.dao=dao;
}
@RequestMapping(value="users/{userId}",method=RequestMethod.GET)
public @ResponseBody Object getUserDetails(@PathVariable String userId){
return dao.getUser(userId);;
}
}
In your UserDAO
do something like this.
@Repository
public class UserDAO {
private final JdbcTemplate jdbcTemplate;
@Autowired
public UserDAO(@Qualifier("sampleDatabase") DataSource dataSource) {
this.jdbcTemplate=new JdbcTemplate(dataSource);
}
// others methods which do the actual queries using Spring JDBC
}
Another thing is in your web.xml
you both have a ContextLoaderListener
and DispatcherServlet
. Now this doesn't have to be a problem but in your case both classes load the same application context resulting in your application being loaded twice with one instance doing nothing.
Remove the ContextLoaderListener
and the context-param
from your web.xml
.
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>MyApp</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Now in your mvc-dispatcher-servlet.xml
add the following 2 lines.
<import resource="classpath:sampledb.xml" />
<import resource="classpath:eventsdb.xml" />
Or move the contents of both files to the mvc-dispatcher-servlet.xml
.
回答2:
You can use a JNDI Configuration its the best practices between you App and Tomcat :
in your xml configuration :
<jee:jndi-lookup id="sampleDatabase" jndi-name="jdbc/sampleDatabase" />
<bean id="sampleDatabaseJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="sampleDatabase" />
<property name="resultsMapCaseInsensitive" value="true" />
<property name="nativeJdbcExtractor" ref="nativeJdbcExtractor" />
</bean>
on your tomcat server.xml you can add below resource under <GlobalNamingResources>
<Resource name="jdbc/sampleDatabase" auth="Container" global="jdbc/sampleDatabase" type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/sampledb"
username="root" password="" maxActive="50" maxWait="-1" maxIdle="10"
validationQuery="SELECT 1 FROM DUAL" testOnBorrow="TRUE" />
on tomcat context.xml :
<ResourceLink auth="Container" global="jdbc/sampleDatabase" name="jdbc/sampleDatabase" type="javax.sql.DataSource"/>
and on you UserDAO :
.....
@Autowired
@Qualifier("sampleDatabaseJdbcTemplate")
private JdbcTemplate sampleDatabaseJdbcTemplate;
...
you can do the same config for the second Database
来源:https://stackoverflow.com/questions/44692318/spring-4-jdbc-how-to-load-db-properties-and-optimize-using-cache-or-db-connec