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

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

Итак, мои вопросы

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

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

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

  4. Должны ли бины DAO быть интерфейсами, поскольку из моей краткой истории со Spring я убедился, что многие (многие / все?) @Autowired beans должны быть? Должен ли я использовать интерфейс 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());
}

}

person Paul    schedule 28.01.2014    source источник
comment
Не имеет отношения к решению, но org.jooq.impl.DAOImpl - это класс, а не интерфейс. Рассматриваемый интерфейс _2 _...   -  person Lukas Eder    schedule 28.01.2014
comment
... Боюсь, вам также нужно будет опубликовать вашу конкретную Configuration конфигурацию   -  person Lukas Eder    schedule 28.01.2014
comment
Спасибо за ответ, Лукас, я добавил свой код класса PersistenceContext. Я предполагаю, что где-то в этой строке мне нужно использовать что-то другое, кроме одноэлементной области для определенных beans / служб / контроллеров? Но это в лучшем случае необразованное предположение. Ура, дружище.   -  person Paul    schedule 28.01.2014


Ответы (1)


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

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

person Lukas Eder    schedule 04.02.2014