Соединение Jooq, Spring и BoneCP закрыто дважды ошибка

Я использую Spring 4.0.0, наряду с jOOQ 3.2.0 и BoneCP 0.8.0 для веб-приложения.

У меня PersistenceContext настроен так же, как это руководство (пожалуйста, прочитайте, это слишком много кода для вставки здесь)

http://www.petrikainulainen.net/programming/jooq/using-jooq-with-spring-configuration/

но с меньшим числом максимальных соединений и closeConnectionWatch = true для проверки ошибок.

Из того, что я могу сделать вывод, это руководство представляет собой не-XML версию собственного руководства на сайте jOOQ, которое можно посмотреть здесь.

http://www.jooq.org/doc/3.2/manual/getting-started/tutorials/jooq-with-spring/

Моя проблема связана с тем, что я не знаю, как использовать сгенерированные jOOQ DAO или аннотацию @Transactional. Я сталкиваюсь с множеством исключений "соединение закрыто дважды", что делает мое приложение принципиально сломанным. Следующая трассировка стека на самом деле не говорит, что она закрыта дважды, но вывод closeConnectionWatch говорит что-то вроде

bonecp connection closed twice detected: first location connection was closed in thread[blah]
closed again in thread[blah2]

Трассировка стека исключений SQL после того, как наблюдение за соединением делает свое дело:

Jan 28, 2014 10:51:51 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/application] threw     exception [Request processing failed; nested exception is org.springframework.jdbc.UncategorizedSQLException: jOOQ; uncategorized SQLException for SQL 
<snip> error code [0]; Connection is closed!; nested exception is java.sql.SQLException: Connection is closed!] with root cause java.sql.SQLException: Connection is closed!
at com.jolbox.bonecp.ConnectionHandle.checkClosed(ConnectionHandle.java:459)
at com.jolbox.bonecp.ConnectionHandle.prepareStatement(ConnectionHandle.java:1011)
at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.invoke(LazyConnectionDataSourceProxy.java:376)
at com.sun.proxy.$Proxy73.prepareStatement(Unknown Source)
at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy$TransactionAwareInvocationHandler.invoke(TransactionAwareDataSourceProxy.java:240)
at com.sun.proxy.$Proxy73.prepareStatement(Unknown Source)
at org.jooq.impl.ProviderEnabledConnection.prepareStatement(ProviderEnabledConnection.java:112)
at org.jooq.impl.SettingsEnabledConnection.prepareStatement(SettingsEnabledConnection.java:76)
at org.jooq.impl.AbstractResultQuery.prepare(AbstractResultQuery.java:224)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:295)
at org.jooq.impl.AbstractResultQuery.fetch(AbstractResultQuery.java:324)
at org.jooq.impl.SelectImpl.fetch(SelectImpl.java:1034)
at org.jooq.impl.DAOImpl.fetch(DAOImpl.java:249)
----> at com.myapplication.spring.services.UserService.authenticate(UserService.java:32)
at com.myapplication.spring.controllers.LoginController.doLogin(LoginController.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:748)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at com.github.dandelion.datatables.core.web.filter.DatatablesFilter.doFilter(DatatablesFilter.java:73)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:931)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Строка, на которой я указал стрелкой, - это строка в службе, которая выполняет вызов в базу данных. У меня есть @Autowired объекты DAO в сервисе, как показано ниже

@Service("UserService")
public class UserService implements UserServiceInterface{

@Autowired UsersDao userDao;
@Autowired PasswordServiceInterface passwordService;

@Override

public Users authenticate(String user,String password) {
    boolean allowedIn = false;

    List<Users> users = userDao.fetch(USERS.USERNAME, user);
            //do something here

Другие функции, которые я использую в подобных сервисах, содержат вызовы с использованием объекта DSLContext, такие как

DSL.select(SOMETHING).from(SOMETABLE).fetch()

DAO и DSLContext хранятся в виде bean-компонентов в PersistenceContext следующим образом. Я автоматически подключаюсь как DSLContext, а не как *Default*DSLContext, так как руководство jOOQ имеет метод тестирования внизу, показывающий только DSLContext.

@Bean
public DefaultDSLContext dsl() {
    return new DefaultDSLContext(configuration());
}

@Bean 
public UsersDao userDao() { //bad because UsersDao isn't an interface???
    return new UsersDao(configuration());
}   

И это контроллер

@Controller
public class LoginController {

@Autowired UserServiceInterface userService;

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
    return "login";
}

@RequestMapping(value = "/login/doLogin", method = RequestMethod.POST)
public String doLogin(@RequestParam("username")String username, @RequestParam("password") String password, HttpSession session) {

    Users u = userService.authenticate(username, password);
    if(u == null) 
        return "redirect:/error";
    else {
        session.setAttribute("user", u.getUserid());
        session.setAttribute("role", u.getRoleid());
        session.setAttribute("retailgroup", u.getGroupid());
        return "redirect:/dashboard";
    }
}

UserService - не единственный сервис, в котором я получаю ошибки - все мои сервисы похожи в том, что они содержат один / оба объекта DAO и DSLContext Autowired, с тем же конструктором конфигурации DAO, что и usersDao(), например productsDao(). Служба Products имеет этот объект DAO и объект DSLContext, и почти такой же, как служба UsersService, которая выполняет вызовы к базе данных.

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

Так что мои вопросы

  1. Где я могу использовать аннотацию @Transactional, и что она делает на самом деле. Означает ли мое отсутствие аннотации @Transactional, что я создаю себе проблемы? Ранее я добавил его во всех местах, где используется БД, но я не могу быть уверен, что это действительно помогает, поскольку я все еще получал те же ошибки.

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

  3. Поскольку connectionPool дважды закрывает соединение, означает ли это, что проблема заключается в том, что поток A и поток B одновременно устанавливают соединение, что-то делают с ним, а затем оба закрывают? Почему это происходит с использованием конфигурации из приведенного выше руководства? Как я могу обеспечить безопасность потока или это не проблема?

  4. Предполагается, что бины DAO должны быть интерфейсами, так как из моей краткой истории со Spring я склонен полагать, что многие (многие / все?) Бины @Autowired должны быть? Должен ли я использовать интерфейс org.jooq.DAOImpl, который является интерфейсом, который, по-видимому, реализуют все сгенерированные jOOQ DAO?

    @Bean
    public org.jooq.impl.DAOImpl usersDao() {
        return new usersDao(configuration());
    }
    

Извиняюсь за длинный вопрос, любая помощь будет принята с благодарностью. Благодарю.

Редактировать: это моя конфигурация в классе PersistenceContext

@Configuration
@PropertySource("classpath:config.properties")
public class PersistenceContext {

@Autowired
private Environment env;


@Bean(destroyMethod = "close")
public DataSource dataSource() {
    BoneCPDataSource dataSource = new BoneCPDataSource();
    dataSource.setDriverClass(env.getRequiredProperty("db.driver"));
    dataSource.setJdbcUrl(env.getRequiredProperty("db.url"));
    dataSource.setUsername(env.getRequiredProperty("db.username"));
    dataSource.setPassword(env.getRequiredProperty("db.password"));
    dataSource.setMaxConnectionsPerPartition(20);
    dataSource.setPartitionCount(2);
    dataSource.setCloseConnectionWatch(true);
    return dataSource;
}

@Bean
public LazyConnectionDataSourceProxy lazyConnectionDataSource() {
    return new LazyConnectionDataSourceProxy(dataSource());
}

@Bean
public TransactionAwareDataSourceProxy transactionAwareDataSource() {
    return new TransactionAwareDataSourceProxy(lazyConnectionDataSource());
}

@Bean
public DataSourceTransactionManager transactionManager() {
    return new DataSourceTransactionManager(lazyConnectionDataSource());
}

@Bean
public DataSourceConnectionProvider connectionProvider() {
    return new DataSourceConnectionProvider(transactionAwareDataSource());
}

@Bean
public JOOQToSpringExceptionTransformer jooqToSpringExceptionTransformer() {
    return new JOOQToSpringExceptionTransformer();
}

@Bean
public DefaultConfiguration configuration() {
    DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
    jooqConfiguration.set(connectionProvider());
    jooqConfiguration.set(new DefaultExecuteListenerProvider(
        jooqToSpringExceptionTransformer()
    ));

    String sqlDialectName = env.getRequiredProperty("jooq.sql.dialect");
    SQLDialect dialect = SQLDialect.valueOf(sqlDialectName);
    jooqConfiguration.set(dialect);

    return jooqConfiguration;
}

@Bean
public DefaultDSLContext dsl() {
    return new DefaultDSLContext(configuration());
}

@Bean
public UsersDao userDao() {
    return new UsersDao(configuration());
}

}

1 ответ

Решение

Перечитав ваш вопрос и ваш чат, я могу сказать, что это действительно наиболее вероятно из-за того, что вы используете версию 3.2.0, в которой была довольно серьезная ошибка:

Эта ошибка была исправлена ​​в 3.2.2, до которой (или позже) вы должны обновить.

Другие вопросы по тегам