我希望在学习Guice的同时创建一个样例项目,它使用JDBC读/写SQL数据库.然而,在使用Spring多年并让它抽象出连接处理和事务之后,我很难从概念上使用它.

我希望有一个服务来启动和停止事务,并调用大量重复使用相同连接和参与相同事务的存储库.我的问题是:

  • 我在哪里创建我的数据源?
  • 如何让存储库访问连接?(本地?)
  • 管理事务的最佳方式(为注释创建拦截器?)

下面的代码显示了我如何在Spring中做到这一点.注入到每个存储库的JdbcOperations将可以访问与活动事务相关联的连接.

除了介绍为事务创建拦截器的教程之外,我找不到很多关于这方面的教程.

我很高兴继续使用Spring,因为它在我的项目中工作得很好,但我想知道如何在纯Guice和JBBC(无JPA/Hibernate/Warp/Reuse Spring)中做到这一点

@Service
public class MyService implements MyInterface {

  @Autowired
  private RepositoryA repositoryA;
  @Autowired
  private RepositoryB repositoryB;
  @Autowired
  private RepositoryC repositoryC; 

  @Override
  @Transactional
  public void doSomeWork() {
    this.repositoryA.someInsert();
    this.repositoryB.someUpdate();
    this.repositoryC.someSelect();  
  }    
}

@Repository
public class MyRepositoryA implements RepositoryA {

  @Autowired
  private JdbcOperations jdbcOperations;

  @Override
  public void someInsert() {
    //use jdbcOperations to perform an insert
  }
}

@Repository
public class MyRepositoryB implements RepositoryB {

  @Autowired
  private JdbcOperations jdbcOperations;

  @Override
  public void someUpdate() {
    //use jdbcOperations to perform an update
  }
}

@Repository
public class MyRepositoryC implements RepositoryC {

  @Autowired
  private JdbcOperations jdbcOperations;

  @Override
  public String someSelect() {
    //use jdbcOperations to perform a select and use a RowMapper to produce results
    return "select result";
  }
}

推荐答案

如果您的数据库不经常更改,您可以使用数据库的JDBC驱动程序附带的数据源,并在提供程序中隔离对第三方库的调用(我的示例使用H2数据库提供的调用,但所有JDBC提供程序都应该有一个).如果您更改为DataSource的不同实现(例如,C3PO、Apache DBCP或应用程序服务器容器提供的实现),则只需编写一个新的提供程序实现,即可从适当的位置获取数据源.在这里,我使用了单例作用域来允许DataSource实例在依赖它的类之间共享(池化是必需的).

public class DataSourceModule extends AbstractModule {

    @Override
    protected void configure() {
        Names.bindProperties(binder(), loadProperties());

        bind(DataSource.class).toProvider(H2DataSourceProvider.class).in(Scopes.SINGLETON);
        bind(MyService.class);
    }

    static class H2DataSourceProvider implements Provider<DataSource> {

        private final String url;
        private final String username;
        private final String password;

        public H2DataSourceProvider(@Named("url") final String url,
                                    @Named("username") final String username,
                                    @Named("password") final String password) {
            this.url = url;
            this.username = username;
            this.password = password;
        }

        @Override
        public DataSource get() {
            final JdbcDataSource dataSource = new JdbcDataSource();
            dataSource.setURL(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }

    static class MyService {
        private final DataSource dataSource;

        @Inject
        public MyService(final DataSource dataSource) {
            this.dataSource = dataSource;
        }

        public void singleUnitOfWork() {

            Connection cn = null;

            try {
                cn = dataSource.getConnection();
                // Use the connection
            } finally {
                try {
                    cn.close();
                } catch (Exception e) {}
            }
        }
    }

    private Properties loadProperties() {
        // Load properties from appropriate place...
        // should contain definitions for:
        // url=...
        // username=...
        // password=...
        return new Properties();
    }
}

要处理事务,应使用支持事务的数据源.我不建议手动实现此功能.但是,使用类似WARP-PERSISTEN或容器提供的事务管理,它将如下所示:

public class TxModule extends AbstractModule {

    @Override
    protected void configure() {
        Names.bindProperties(binder(), loadProperties());

        final TransactionManager tm = getTransactionManager();

        bind(DataSource.class).annotatedWith(Real.class).toProvider(H2DataSourceProvider.class).in(Scopes.SINGLETON);
        bind(DataSource.class).annotatedWith(TxAware.class).to(TxAwareDataSource.class).in(Scopes.SINGLETON);
        bind(TransactionManager.class).toInstance(tm);
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), new TxMethodInterceptor(tm));
        bind(MyService.class);
    }

    private TransactionManager getTransactionManager() {
        // Get the transaction manager
        return null;
    }

    static class TxMethodInterceptor implements MethodInterceptor {

        private final TransactionManager tm;

        public TxMethodInterceptor(final TransactionManager tm) {
            this.tm = tm;
        }

        @Override
        public Object invoke(final MethodInvocation invocation) throws Throwable {
            // Start tx if necessary
            return invocation.proceed();
            // Commit tx if started here.
        }
    }

    static class TxAwareDataSource implements DataSource {

        static ThreadLocal<Connection> txConnection = new ThreadLocal<Connection>();
        private final DataSource ds;
        private final TransactionManager tm;

        @Inject
        public TxAwareDataSource(@Real final DataSource ds, final TransactionManager tm) {
            this.ds = ds;
            this.tm = tm;
        }

        public Connection getConnection() throws SQLException {
            try {
                final Transaction transaction = tm.getTransaction();
                if (transaction != null && transaction.getStatus() == Status.STATUS_ACTIVE) {

                    Connection cn = txConnection.get();
                    if (cn == null) {
                        cn = new TxAwareConnection(ds.getConnection());
                        txConnection.set(cn);
                    }

                    return cn;

                } else {
                    return ds.getConnection();
                }
            } catch (final SystemException e) {
                throw new SQLException(e);
            }
        }

        // Omitted delegate methods.
    }

    static class TxAwareConnection implements Connection {

        private final Connection cn;

        public TxAwareConnection(final Connection cn) {
            this.cn = cn;
        }

        public void close() throws SQLException {
            try {
                cn.close();
            } finally {
                TxAwareDataSource.txConnection.set(null);
            }
        }

        // Omitted delegate methods.
    }

    static class MyService {
        private final DataSource dataSource;

        @Inject
        public MyService(@TxAware final DataSource dataSource) {
            this.dataSource = dataSource;
        }

        @Transactional
        public void singleUnitOfWork() {
            Connection cn = null;

            try {
                cn = dataSource.getConnection();
                // Use the connection
            } catch (final SQLException e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    cn.close();
                } catch (final Exception e) {}
            }
        }
    }
}

Database相关问答推荐

包含接受Cassandra中多个数据的语句

安装postgresql用于使用和调试Apache-AGE

如何理解mysql explain 命令

Android Room持久性库@Update不起作用

nodejs和数据库如何通信 ?

无法在 MYSQL 5.5 w/MYSQL Workbench 中更改模式名称

为什么String or Binary data would be truncated不是更具描述性的错误?

PHP 和 MySQL Select 单个值

用 Python 永久存储字典的优雅方式?

postgresql 在 where 子句中使用 json 子元素

什么是数据仓库?

有什么理由不应该在生产中使用 h2 数据库?

限制来自本地主机的 MySQL 连接以提高安全性

维护 mgo 会话的最佳实践

如何将特定的、可变的 order订单保存到数据库中

如何在每个 SQLite 行中插入唯一 ID?

App=EntityFramework 在 Sql 连接字符串中有什么作用?

Web 应用程序的文件存储:文件系统、数据库和 NoSQL 引擎

什么是 ACID 的真实示例?

如何在 Java 中检测 SQL 表的存在?