【问题标题】:JOOQ and SpringJOOQ 和 Spring
【发布时间】:2011-05-27 08:07:49
【问题描述】:

有没有人尝试过将 JOOQ 与 Spring 框架一起使用,或者我是否正在开辟新天地?

http://www.jooq.org

【问题讨论】:

  • 由于 jOOQ 本身很新,您很可能会开辟新天地。您希望如何连接这两个库?您是否打算使用 spring 作为配置 jOOQ 的一种手段?我很好奇你的用例
  • 您好 Lukas,我没有特别的用例。我问这个问题,看看其他人有没有一起使用过这两个库,有没有什么值得分享的经验。我可以看到我可以使用 Spring 配置的数据源来提供到 jOOQ 工厂的连接。除此之外,我不确定更深层次的集成是否有任何优点,但我是 Spring 的新手,所以喜欢通过示例学习。

标签: java sql spring jooq


【解决方案1】:

是的,很多人(现在)都有。 jOOQ 手册包括一个关于如何开始使用jOOQ、Spring、Spring-TX 和BoneCP 的教程:

Petri Kainulainen 也有一个非常好的教程,解释了设置项目的每一步,在这里:

这是一篇关于如何在 Spring Boot 中使用 jOOQ 的博客文章,当您需要 jOOQ 的商业发行版时特别有用:

【讨论】:

    【解决方案2】:

    我希望将 jOOQ 用作构建器库,以提供对 Spring 的 JdbcTemplate 和相关类的查询。不幸的是,jOOQ 似乎将两个概念结合到同一组类中:SQL 生成和查询执行。就我而言,我想要前者,但想让 Spring 处理后者。不过,它确实有效。例如,您可以执行以下操作(使用 jOOQ 2.x API):

    Factory create = new Factory(null, SQLDialect.ORACLE);
    getJdbcTemplate().query(
        create.select(create.field(ID_COL),
                      create.field(VALUE_COL))
            .from(FOO_TABLE)
            .where(create.field(ID_COL).equals("ignored"))
            .getSQL(),
        myRowMapper,
        id);
    

    【讨论】:

    • 这是一个很好的集成示例。为什么选择 spring 来执行查询?因为 jOOQ 缺少行映射功能?还是缺少事务处理?
    • @LukasEder 对我来说它的事务处理。我广泛使用 AspectJ + Spring(很抱歉跟踪...只是碰巧碰到另一个问题。由您回答)。
    • @AdamGent:Spring 的事务处理可以与 jOOQ 的查询执行一起使用。这已在 jOOQ 用户组上得到证明。不过,手册中应该有一个关于此的部分...
    • @LukasEder 在理论上似乎很有可能。可以只在您的工厂周围制作 try...catch 异常装饰器并将它们转换为 Spring Exceptions(用于事务管理)。您能否添加此帖子的链接,因为它对 OP 似乎很有用。
    • @AdamGent:或者只是将ExecuteListener 挂钩到jOOQ 中,它负责将jOOQ 的DataAccessException 转换为您需要的任何Spring 异常......这大约需要5-10 行代码。见docs here
    【解决方案3】:

    假设你正在使用 Spring 构建一个 webapp,你可能想要做这样的事情:

    try {
      Connection conn = dataSource.getConnection();
      try {
        // Do something with JOOQ
        // No need to use a JdbcTemplate!
      }
      finally {
        if (conn != null) {
          conn.close();
        }
      }
    } catch (SQLException e) {
      // your error handling
    }
    

    您可能希望通过 Spring 的依赖注入获取 DataSource,因为您的 Web 容器 Tomcat 或其他任何东西都在提供 DataSource 并进行连接池。在您的一个弹簧配置文件中,您将拥有类似

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    

    上述代码所在的对象(或为该代码提供数据源的某个对象)可以在 spring 文件中配置以使用数据源实例化它,例如

    <bean id="fooService" class="com.fubar.FooServiceImpl">
      <constructor-arg ref="dataSource" type="javax.sql.DataSource" />
    </bean>
    

    字符串“jdbc/datasource”的部分将对应于 Web 容器中配置的资源名称。这会有所不同,但对于 Tomcat,它可能是 Tomcat home 下的 conf/Catalina/localhost 中的上下文文件,例如,

    <?xml version="1.0" encoding="UTF-8"?>
    <Context debug="10" reloadable="true" useNaming="true" antiJARLocking="true">
        <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource"
            maxActive="100" maxIdle="30" maxWait="10000" validationQuery="SELECT 1"
            username="foo" password="fubar" driverClassName="org.postgresql.Driver" 
            url="jdbc:postgresql://localhost/foobase"/>         
    </Context>
    

    【讨论】:

    • 当 JdbcTemplate 可以在更少的行中为您完成连接处理并且出错的风险更小时,为什么您自己进行连接处理?
    【解决方案4】:

    希望这对某人有所帮助....

    Spring 应用上下文配置。

     <bean id="propertyConfigurer" 
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="systemPropertiesModeName">
                <value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
            </property>
            <property name="searchSystemEnvironment">
                <value type="boolean">true</value>
            </property>
        </bean>
    
    
    
        <bean id="dataSource" 
            class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
            <property name="driverClassName" value="org.h2.Driver"/>
            <property name="url" 
            value="jdbc:h2://${user.home}
            ${file.separator}tracciabilitaCanarini${file.separator}db${file.separator}basedb"/>
            <property name="username" value="sa"/>
            <property name="password" value="sa"/>
        </bean>
    
        <bean id="datasourceConnection" 
         class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" 
          lazy-init="true" depends-on="dataSource">
            <property name="targetObject">
                <ref bean="dataSource"/>
            </property>
            <property name="targetMethod">
                <value>getConnection</value>
            </property>
        </bean>
    
        <bean id="publicFactory" class="dbLayer.db.PublicFactory" lazy-init="true"
          depends-on="datasourceConnection" >
            <constructor-arg index="0" ref="datasourceConnection"  />
        </bean>
    

    它将使用给定的连接自动填充公共工厂(是的,它可以是一个池连接,具有自动关闭等功能,请参阅 DriverManagerDataSource 类以获得更详细的配置)。 现在,publicFactory。注意:jOOQ 生成的原始公共工厂无需修改。

    /**
     * This class is generated by jOOQ
     */
    package dbLayer.db;
    
    /**
     * This class is generated by jOOQ.
     */
    @javax.annotation.Generated(value    = {"http://www.jooq.org", "2.0.5"},
                                comments = "This class is generated by jOOQ")
    public class PublicFactory extends org.jooq.util.h2.H2Factory {
    
        private static final long serialVersionUID = -1930298411;
    
        /**
         * Create a factory with a connection
         *
         * @param connection The connection to use with objects created from this factory
         */
        public PublicFactory(java.sql.Connection connection) {
            super(connection);
        }
    
        /**
         * Create a factory with a connection and some settings
         *
         * @param connection The connection to use with objects created from this factory
         * @param settings The settings to apply to objects created from this factory
         */
        public PublicFactory(java.sql.Connection connection, org.jooq.conf.Settings settings) {
            super(connection, settings);
        }
    }
    

    最后,只需调用工厂。

     PublicFactory vs = (PublicFactory) SpringLoader.getBean("publicFactory");
        SimpleSelectQuery<VersionRecord> sq = vs.selectQuery(dbLayer.db.tables.Version.VERSION);
        VersionRecord v = null;
                    try {
                        v = sq.fetchAny();
                    } catch (Exception e) {
                        log.warn("Seems that version table does not exists!", e);
                    }
    

    完成!

    【讨论】:

    • !!!!! 这是一个非常糟糕的主意。 Spring 通常假定它创建的 bean 是单例的,因此是线程安全的。 PublicFactory 不是线程安全的!您需要正确标记您的 bean as scope 才能使其正常工作,即使您这样做,我也不推荐它。
    【解决方案5】:

    让 jOOQ 与 spring 一起工作你需要做/知道的所有事情:

    1. 获取事务管理器绑定到线程的java.sql.Connection
    2. 通过异常翻译正确处理事务
    3. 了解 jOOQ Factory 对象(尽管有名称)不是线程安全的。因此每次使用都需要实例化一个新对象(不要这样做answer)。

    因此,对于第一种和第二种情况,我提供以下要点:https://gist.github.com/3669307 的作用是 Lukas recommends

    对于第三种情况,您可以基本上创建一个工厂的工厂(其中包含 DataSource),也可以使用 spring 组件中的有线 DataSource 在每个方法中实例化一个新的 Factory 对象。

    @Service
    public class MyDaoOrService {
        @Autowired
        private void DataSource dataSource;
    
        @Transactional
        public void doSomeJooq(){
            Settings s = new Settings();
            //You could instead put this jooq configuration xml
             s.getExecuteListeners().add("com.snaphop.jooq.SpringExceptionTranslationExecuteListener");
            MyGeneratedFactory f = new MyGeneratedFactory(dataSource, s);
            f.select(); //etc
        }
    }
    

    至于设置监听器,您可以通过 JOOQ 的配置支持来避免程序化创建。

    我不会介绍如何在 Spring 中设置 DataSource,因为它在无数其他/更好的地方都有介绍。

    【讨论】:

    • 这个答案使用 jOOQ 2.x API。我认为这值得一提,或者将 API 使用更改为 3.x...
    【解决方案6】:

    使用 jOOQ 运行 spring 事务要简单得多(除非我忘记了什么):

    只需将您的数据源包装到

    org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
    

    可选:延迟打开 jdbc 连接,直到第一个实际的 sql 语句发生使用

    org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
    

    作为一个示例,这样做是为了创建一个应用了“事务”和“惰性”的 jOOQ 工厂

    DataSource rawDS = /* your actual data source */
    // (optional) make access lazy
    final DataSource lazyDS = new LazyConnectionDataSourceProxy(rawDataSource);
    // make spring transactions available in plain jdbc context
    final DataSource txDS = new TransactionAwareDataSourceProxy(lazyDS);
    // create jOOQ factory
    Factory jooq = new Factory(txDS, /* dialect */, /* settings */)
    // voila!
    

    【讨论】:

    • 一个带有工作演示的小型 github repo 将是一个很大的帮助!
    【解决方案7】:

    这里给出了最简单的方法,(我发现)将 Spring Transactions 与 jOOQ 一起使用:http://blog.liftoffllc.in/2014/06/jooq-and-transactions.html

    看看这个答案以获得更好的解释:https://stackoverflow.com/a/24380508/542108

    【讨论】:

    • 仅链接的答案很糟糕,因为链接会腐烂。
    【解决方案8】:

    对于 Java 配置(这是 Spring Boot 的默认配置),您可以使用以下代码:

    /* JOOQ Configuration */
    @Bean
    public DataSourceConnectionProvider dataSourceConnectionProvider() {
        return new DataSourceConnectionProvider(dataSource());
    }
    
    @Bean
    public DefaultConfiguration defaultConfiguration() {
        DefaultConfiguration defaultConfiguration = new DefaultConfiguration();
        defaultConfiguration.setConnectionProvider(dataSourceConnectionProvider());
        defaultConfiguration.setSQLDialect(SQLDialect.POSTGRES);
        return defaultConfiguration;
    }
    
    @Bean
    public DSLContext dslContext() {
        return new DefaultDSLContext(defaultConfiguration());
    }
    

    【讨论】:

      猜你喜欢
      • 2012-09-17
      • 2017-02-20
      • 2020-09-23
      • 2013-11-11
      • 2018-01-07
      • 2020-04-14
      • 2019-09-10
      • 1970-01-01
      • 2020-09-15
      相关资源
      最近更新 更多