Spring 4 JDBC - 如何加载数据库属性和优化(使用缓存或数据库连接池)

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

正在维护用 Spring MVC 4.3 编写的代码库。9.RELEASE(不是 Spring 引导)...

在src/main/resources之下:

有两个不同的数据库配置文件:

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>

示例网络服务代码:

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;
        }
}

用户道:

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
}

之前的作者在每个 DAO 中都使用了这种模式(使用 ApplicationContext 初始化数据库)(代码库中有大约 20 个不同的 DAO,每个都使用相同的两个数据库配置文件做同样的事情)!

问题:

  1. 这似乎很不充分(似乎应该做一次),这个(加载基于 Spring 的数据库配置文件)如何在 war 后立即完成一次文件加载到 Tomcat?

  2. 提高性能的最佳技术是什么(例如,我应该使用缓存系统还是数据库连接池)?

非常感谢任何建议...

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();
        }
    }

这段代码很危险,根据你上下文的大小,你最终会运行陷入问题。这里发生的是你每次需要一个对象时加载整个应用程序,你将打开到数据库的连接(由于连接太多最终将停止工作)你会遇到奇怪的事务问题并且可能(取决于大小) 内存问题。 (当然,如果那是你想要的,一定要像这样进行)。

相反,您应该使用依赖注入。将所有需要的依赖项声明为字段并让 spring 进行自动连接,这只会在启动时发生一次。

@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);;
    }
}

在你的 UserDAO 中做这样的事情。

@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
}

另一件事是你的 web.xml 你们都有 ContextLoaderListenerDispatcherServlet。现在这不一定是个问题,但在你的情况下 类 加载相同的应用程序上下文导致你的应用程序被加载两次,一个实例什么都不做。

从您的 web.xml 中删除 ContextLoaderListenercontext-param

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

现在在您的 mvc-dispatcher-servlet.xml 中添加以下 2 行。

<import resource="classpath:sampledb.xml" />
<import resource="classpath:eventsdb.xml" />

或者将两个文件的内容移动到mvc-dispatcher-servlet.xml

您可以使用 JNDI 配置,这是您的 App 和 Tomcat 之间的最佳实践:

在您的 xml 配置中:

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

在您的 tomcat server.xml 上,您可以在 <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" />

在 tomcat context.xml :

<ResourceLink auth="Container" global="jdbc/sampleDatabase" name="jdbc/sampleDatabase" type="javax.sql.DataSource"/>

还有你的 UserDAO:

.....

@Autowired
@Qualifier("sampleDatabaseJdbcTemplate")
private JdbcTemplate sampleDatabaseJdbcTemplate;

...

您可以对第二个数据库进行相同的配置