Spring 4 JDBC - How to load DB Properties and optimize (using Cache or DB Connection Pool)

半腔热情 提交于 2019-12-13 03:43:53

问题


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):

  1. 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?

  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!