java - Spring Controller 中的 JDBCTemplate

标签 java spring spring-mvc jdbctemplate

我需要做一些并非严格意义上的实体列表的报告。更多自由形式的查询,硬编码 SQL,以任意 JSON 形式返回。

我正在尝试为此使用 JdbcTemplate,并使用我在网上找到的代码(例如 here)来构建它。

这一切都在一个名为 ReportViewController 的类中。

我的代码(带有稍后相关的日志记录/调试问题):

private JdbcTemplate jdbcTemplate;
private DataSource dataSource;
private UUID uuid;


public ReportViewController() {
    this.uuid = UUID.randomUUID();
}


public void setDataSource(DataSource dataSource) {
    System.out.println("Setting data source");
    this.dataSource = dataSource;
    if (this.dataSource == null) {
        System.out.println("failed to set my dataSource properly");
    }
    System.out.println("uuid: " + this.uuid);
}

@RequestMapping(value = "/report/{reportId}", method = RequestMethod.POST)
@ResponseBody
public String runNewUntypedSearch(@PathVariable Integer reportId, WebRequest request, HttpSession session) {
    //View v = View.findView(reportId); 

    System.out.println("uuid: " + this.uuid);
    if (this.dataSource == null) {
        System.out.println("lost my dataSource somewhere along the way");
    }

    this.jdbcTemplate = new JdbcTemplate(this.dataSource);

    List<Map<String, Object>> l = null;
    try {
        l = this.jdbcTemplate.queryForList("select 1 as one");

        JSONSerializer s = new JSONSerializer();
        return s.serialize(l);

    } catch (Exception e) {
        System.out.println("querying failed -- " + e.toString());
        return "";
    }
} 

我的问题是,尽管控制台指示它正在调用 setDataSource(),但当我进入 runNewUntypedSearch() 时,这些属性为空。

因此,我怀疑我正在与之交谈的对象存在犯规行为,我让这个 Controller 在其构造函数中创建一个 UUID,并将其作为其日志记录的一部分输出,如您在上文中所见。

控制台输出部分显示:

Setting data source
hash: bc679ef1-fbdc-4ef9-9457-dd79889f973e
...
hash: bbe2b03a-92ff-4517-add8-8d9ff6480cc8
lost my dataSource somewhere along the way

...然后 Spring 以“内部错误”和堆栈跟踪响应浏览器,该堆栈跟踪指示我正在尝试实例化我的 jdbcTemplate 的空指针。

这使我得出结论,无论对象被告知设置其数据源与作为 URL 映射方法挂接的实例不同。服务器运行的“真实”实例尚未调用 getDataSource()

可以吗?为什么这不起作用?

编辑:

web.xml(编辑删除我客户的名字):

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


    <display-name>myProjectName</display-name>

    <description>Roo generated application</description>


    <!-- Enable escaping of form submission contents -->
    <context-param>
        <param-name>defaultHtmlEscape</param-name>
        <param-value>true</param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
    </context-param>

    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>



    <filter>
        <filter-name>HttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>



    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter>
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>HttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>



    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Handles Spring requests -->
    <servlet>
        <servlet-name>myProjectName</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>myProjectName</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <session-config>
        <session-timeout>90</session-timeout>
    </session-config>

    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/uncaughtException</location>
    </error-page>

    <error-page>
        <error-code>404</error-code>
        <location>/resourceNotFound</location>
    </error-page>
</web-app>

applicationContext.xml(编辑以删除客户端名称和 Roo 生成的注释)

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd         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         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">


    <context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>


    <context:spring-configured/>


    <context:component-scan base-package="com.myProjectName">
        <context:exclude-filter expression=".*_Roo_.*" type="regex"/>
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>


    <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
        <property name="driverClassName" value="${database.driverClassName}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
        <property name="testOnBorrow" value="true"/>
        <property name="testOnReturn" value="true"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
        <property name="numTestsPerEvictionRun" value="3"/>
        <property name="minEvictableIdleTimeMillis" value="1800000"/>
        <property name="validationQuery" value="SELECT 1"/>
    </bean>
    <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
    <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
        <property name="persistenceUnitName" value="persistenceUnit"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean class="org.springframework.mail.javamail.JavaMailSenderImpl" id="mailSender">
        <property name="host" value="${email.host}"/>
    </bean>
    <bean class="org.springframework.mail.SimpleMailMessage" id="templateMessage">
        <property name="from" value="${email.from}"/>
        <property name="subject" value="${email.subject}"/>
    </bean>
</beans>

webmvc-config.xml(编辑以删除我客户的名字):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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     http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <!-- The controllers are autodetected POJOs labeled with the @Controller annotation. -->
    <context:component-scan base-package="com.myProjectName" use-default-filters="false">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>


    <!-- Turns on support for mapping requests to Spring MVC @Controller methods
         Also registers default Formatters and Validators for use across all @Controllers -->
    <mvc:annotation-driven conversion-service="applicationConversionService"/>

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources -->
    <mvc:resources location="/, classpath:/META-INF/web-resources/" mapping="/resources/**"/>

    <!-- Allows for mapping the DispatcherServlet to "/" by forwarding static resource requests to the container's default Servlet -->
    <mvc:default-servlet-handler/>

    <!-- register "global" interceptor beans to apply to all registered HandlerMappings -->
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
    </mvc:interceptors>

    <!-- selects a static view for rendering without the need for an explicit controller -->
    <mvc:view-controller path="/login"/>
    <mvc:view-controller path="/admin" view-name="admin"/>
    <mvc:view-controller path="/" view-name="index"/>
    <mvc:view-controller path="/uncaughtException"/>
    <mvc:view-controller path="/resourceNotFound"/>
    <mvc:view-controller path="/dataAccessFailure"/>

    <!-- Resolves localized messages*.properties and application.properties files in the application to allow for internationalization. 
        The messages*.properties files translate Roo generated messages which are part of the admin interface, the application.properties
        resource bundle localizes all application specific messages such as entity names and menu items. -->
    <bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="messageSource" p:basenames="WEB-INF/i18n/messages,WEB-INF/i18n/application" p:fallbackToSystemLocale="false"/>

    <!-- store preferred language configuration in a cookie -->
    <bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver" id="localeResolver" p:cookieName="locale"/> 

    <!-- resolves localized <theme_name>.properties files in the classpath to allow for theme support -->
    <bean class="org.springframework.ui.context.support.ResourceBundleThemeSource" id="themeSource"/>

    <!-- store preferred theme configuration in a cookie -->
    <bean class="org.springframework.web.servlet.theme.CookieThemeResolver" id="themeResolver" p:cookieName="theme" p:defaultThemeName="standard"/>

    <!-- This bean resolves specific types of exceptions to corresponding logical - view names for error views. 
         The default behaviour of DispatcherServlet - is to propagate all exceptions to the servlet container: 
         this will happen - here with all other types of exceptions. -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException">
        <property name="exceptionMappings">
            <props>
                <prop key=".DataAccessException">dataAccessFailure</prop>
                <prop key=".NoSuchRequestHandlingMethodException">resourceNotFound</prop>
                <prop key=".TypeMismatchException">resourceNotFound</prop>
                <prop key=".MissingServletRequestParameterException">resourceNotFound</prop>
            </props>
        </property>
    </bean>

    <!-- allows for integration of file upload functionality -->
    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>
  <bean class="com.mavenmanagement.web.ApplicationConversionServiceFactoryBean" id="applicationConversionService"/> 
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
  </bean>
    <bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
    <property name="definitions">
      <list>
        <value>/WEB-INF/layouts/layouts.xml</value>
        <!-- Scan views directory for Tiles configurations -->
        <value>/WEB-INF/views/**/views.xml</value>
      </list>
    </property>
  </bean>
</beans>

我看到注释扫描上的过滤在 applicationContext 级别阻止了 @Controller,并在 webmvc-config 级别启用了它们。所以我认为这并不能解释双重实例化。

最佳答案

@Tomasz Nurkiewicz 和@Inerdial 在问题评论中的提示让我想到了完全不同的方式,这更简单并且工作出色,但我还没有在任何地方看到它的记录。

在 applicationContext.xml 的底部,我正在实例化一个 JdbcTemplate 对象:

   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
    <property name="dataSource" ref="dataSource"/> 
   </bean>

然后我只是将它@AutoWire 到我需要它的 Controller 中:

@Autowired
private JdbcTemplate jdbcTemplate;

Bob 的你叔叔,我现在可以与 this.jdbcTemplate 中的完全配置的模板对象对话。

为什么这个 Controller 类被实例化两次是因为我从 applicationContext.xml 启动它以尝试配置它的数据源。然后创建了由 @Controller 注释实例化的版本,但没有配置。这就是问题所在。

我还听从了评论中的其他建议,将此查询函数移动到模型类型类中,并从我的 View Controller 中调用它。是的,显然 View Controller 中的数据库交互是可憎的。

关于java - Spring Controller 中的 JDBCTemplate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9036853/

相关文章:

java - addArgumentResolver 未在配置类中调用

java - Spring MVC 测试结果出现 415 错误

java - 将第三方库与 Maven 一起使用

java - 为了安全起见,我是否需要增加等待时间才能获得安静的服务响应?

java - 在 Windows 命令行参数中使用空格

java - 如何在管理页面将用户添加到团队

java - 我收到“存在意外错误(类型=未找到,状态=404)”错误?

java - 仅当作为参数传递的 List 包含元素时才考虑 JPA 'where in'

java - Spring-JPA-Hibernate如何使用EntityManager

java - 如何在运行时在 Spring Boot 上运行 CUSTOM DDL DML ...,而不强制 Hibernate