JDBCTemplate в Spring Controller - PullRequest
       7

JDBCTemplate в Spring Controller

2 голосов
/ 27 января 2012

Мне нужно сделать несколько отчетов, которые не являются строго списком лиц.Более произвольный запрос, жестко запрограммированный SQL, возвращается как произвольный JSON.

Я пытаюсь использовать JdbcTemplate для этого и использую код, который я нашел в Интернете (например, здесь ), чтобы построить это.

Это все внутри классапод названием 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 (), эти свойства являются нулевыми.

Итак, подозревая нечестную игру с точки зрения объектов, с которыми я разговариваю, у меня есть этот контроллер, создающий 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.

Это приводит меня к выводу, что любой объект, которому говорят установить свой dataSource, не является тем же экземпляром, который подключается как метод сопоставления 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>

Я вижу, что фильтрация при сканировании аннотацииблокирует @Controller s на уровне applicationContext и включает их на уровне webmvc-config.Так что, я думаю, это не объясняет двойную реализацию.

Ответы [ 2 ]

2 голосов
/ 31 января 2012

Подсказки @Tomasz Nurkiewicz и @Inerdial в комментариях к вопросу заставили меня подумать совсем по-другому, это намного проще и блестяще сработало, и все же я нигде не видел, чтобы это было задокументировано.В нижней части applicationContext.xml, я создаю экземпляр объекта JdbcTemplate:

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

Затем я просто @AutoWire это в контроллер, где мне это нужно:

@Autowired
private JdbcTemplate jdbcTemplate;

И Бобдядя, теперь я могу поговорить с полностью настроенным объектом шаблона в this.jdbcTemplate.

Почему этот класс контроллера дважды создавался, потому что я запускал его из applicationContext.xml, чтобы попытаться настроить его источник данных.Затем была создана версия, созданная аннотацией @Controller, но не настроенная.Так что в этом была проблема.

Я также последовал другому совету в комментариях и переместил эту функцию запроса в класс типа модели и вызвал ее из моего контроллера представления.Да очевидно взаимодействие БД в контроллере представления - это мерзость.

2 голосов
/ 27 января 2012

Прежде всего - в приложении Spring MVC есть два контекста приложения - основной загружается через web.xml и ContextLoaderListener и дочерний контекст, создаваемый каждым DispatcherServlet (обычно одним).

Ваш компонент контроллера создается в обоих контекстах, вы устанавливаете источник данных в контроллере, созданном в главном контексте, но Spring MVC использует источник, созданный в дочернем контексте.

Очевидно, что в дочернем контексте должна быть только одна копия.

  • Если вы используете обычную XML-конфигурацию, определение компонента контроллера должно быть помещено в контекст *-servlet.xml, а не в applicationContext.xml (если вы придерживаетесь именования по умолчанию).

  • Если вы используете компонентное сканирование, убедитесь, что основной контекст не сканирует пакеты или классы контроллера, помеченные @Controller

Если выуверены, что только один экземпляр создан, он должен работать.Обратите внимание, что контроллер может получить доступ к DataSource (дочерний контекст может получить доступ к основному контексту), но не наоборот.

Примечание 1

В подобных случаях достаточно напечатать:

System.out.println(this);

В контроллере.По умолчанию toString() должен давать разные результаты для разных экземпляров.

Примечание 2

Ваша обработка ошибок должна быть улучшена.Если это не только в качестве примера, всегда регистрируйте полную трассировку стека исключений и избегайте возврата фиктивных результатов, таких как пустая строка, которые, как правило, скрывают ошибки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...