【问题标题】:Automatically opening and closing connection自动打开和关闭连接
【发布时间】:2010-08-24 02:02:28
【问题描述】:

注意:请忽略我使用 <i>MultivaluedMap</i> 而不是多个 vargs String...args

java中有标准的方法吗?

我拥有的是从远程服务器返回的资源。但是在每次查询之前,远程连接必须打开,并且在返回返回之后 - 它必须关闭。

所以一个自然的方式是这样的:

Connection c = config.configureConnection();
c.open();       //open
List<Car> cars;
try{
   cars = c.getCars();
}finally{
   c.close();   //close
}

现在我想实现一些在资源本身级别上运行的东西,而不用担心连接,例如:

List<Car> cars = new CarResource().all(); //opens and closes connection

我目前的做法是拥有一个抽象类,AbstractQueriable 调用抽象方法 query(String ...args)query(int id),任何扩展它的类都必须实现。

AbstractQuerieable 实现了 Queriable 接口,这使得它暴露了三个公共方法 filter(String ...args), all()get(int id) - 这是面向公众的方法。

这里是查询接口:

public interface Queriable <T>{
    public T get(String id);
    /** Simply returns all resources */
    public Collection<T> all(); 
    public Collection<T> filter(MultivaluedMap<String, String> args);   
}

这是实现它的 AbstractQueriable 类:

public abstract class AbstractQueriable<T> implements Queriable<T> {

@Override
public final T get(String id) {
    setup();
    try {
        return query(id);
    } finally {
        cleanup();
    }
}

@Override
public final Collection<T> filter(MultivaluedMap<String, String> args) {
    setup();
    try {
            return query(args);
    } finally {
        cleanup();
    }
}

/**
 * Returns all resources.
 * 
 * This is a convenience method that is equivalent to passing an empty
 * arguments list to the filter function.
 * 
 * @return The collection of all resources if possible
 */
    @Override
public final Collection<T> all() {      
    return filter(null);
}

/**
 * Queries for a resource by id.
 * 
 * @param id
 *            id of the resource to return
 * @return
 */
protected abstract T query(String id);

/**
 * Queries for a resource by given arguments.
 * 
 * @param args
 *            Map of arguments, where each key is the argument name, and the
 *            corresponing values are the values
 * @return The collection of resources found
 */
protected abstract Collection<T> query(MultivaluedMap<String, String> args);

private void cleanup() {
    Repository.close();
}

private void setup() {
    Repository.open();
}

最后,我想在代码中使用的资源必须扩展 AbstractQueriable 类,例如(请注意,这些方法的细节并不重要):

public class CarRepositoryResource extends AbstractQueriable<Car> {

    @Override
    protected Car query(String id) {
        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
        params.add("CarID", id);

        // Delegate the query to the parametarized version
        Collection<cars> cars = query(params);
        if (cars == null || cars.size() == 0) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        }
        if (cars.size() > 1) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        }
        return cars.iterator().next();
    }

    @Override
    protected Collection<Car> query(MultivaluedMap<String, String> params) {
        Collection<Car> cars = new ArrayList<Car>();        

        Response response = Repository.getConnection().doQuery("Car");
        while (response.next()) {
            Returned returned = response.getResult();
            if (returned != null) {
                cars.add(returned);
            }
        }
        return cars;
    }

}

最后,我可以在我的代码中使用:

Collection<Car> cars = new CarRepositoryResource().all();
//... display cars to the client etc...

我不喜欢这种设置:

  1. 每次执行查询时,我都必须实例化我的“CarRepositoryResource”的一个新实例。
  2. 方法名称“query”虽然是内部的和私有的,但仍然令人困惑和笨拙。
  3. 我不确定是否有更好的模式或框架。

我使用的连接不支持/实现 JDBC api 并且不是基于 sql 的。

【问题讨论】:

  • 您可以考虑使用 AOP 或代理来透明地处理连接,例如使用 Spring 或(可能)Guice。
  • 谢谢,我实际上正在寻找一些类似的建议。你能用一些好的起点把它变成一个答案吗?

标签: java design-patterns


【解决方案1】:

您可以使用(臭名昭著的)Open session in view 模式的变体。

基本上归结为:

  1. 定义连接可用的“上下文” (通常是 Web 应用程序中的请求)
  2. 在进入/退出上下文时处理(可能是惰性的)初始化和释放连接
  3. 将您的方法编码为理所当然,它们只会在这样的上下文中使用

实现起来并不困难(将连接存储在静态 ThreadLocal 中以使其线程安全),并且肯定会节省一些打开/关闭调用(从性能方面来说,这可能是一个很大的收获,具体取决于您的连接有多大是)。

上下文类可能看起来像(考虑这个伪代码);

public class MyContext{
  private static final
  ThreadLocal<Connection> connection = new ThreadLocal<Connection>();

  public static void enter() {
     connection.set(initializeConnection());
     // this is eager initialization
     // if you think it will often the case that no connection is actually
     // required inside a context, you can defer the actual initialization
     // until the first call to get()
  }

  public static void exit() {
    try { connection.close(); }
    catch(Throwable t) { /* panic! */ }
    finally { connection.set(null); }
  }

  public static Connection get() {
    Connection c = connection.get();
    if (c == null) throw new IllegalStateException("blah blah");
    return c;
  }
}

然后你会使用这样的连接:

MyContext.enter();
try {
   // connections are available here:
   // anything that calls MyContext.get()
   // gets (the same) valid connection instance
} finally {
  MyContext.exit();
}

这个块可以放在你想要的任何地方(在 webapps 中它通常包装每个请求的处理) - 如果你正在编写一个简单的案例,当你想要一个单一的连接在应用程序的整个生命周期中可用时,从 main 方法,到 API 中最好的方法。

【讨论】:

  • 有趣。所以在我的简单示例中,它将类似于: MyContext.enter();尝试 { Collection 汽车 = Car.all(); } 最后 { MyContext.exit(); }
  • 您可以选择在 enter()/exit() 之间执行多个查询,例如通过您的连接进行多个查询(例如:通过 id 获取汽车,然后列出所有以前的所有者 -通过相同的连接)甚至消耗结果。 (继续)
  • 后者是一个有趣的选项(类似于在 hibernate 中使用视图模式中的打开会话所做的): Car.all() 可以返回一个惰性列表(仅当/如果实际需要,可能扩展 AbstractSequentialList)。这将需要支持在同一连接上执行并发查询,或者(如果您的连接不是事务性的并且不是太重而无法打开/关闭)每个上下文使用多个连接(然后您将在 Context.exit() 中将它们全部关闭)。 (继续)
  • 该模式的优点是它将资源管理(打开/关闭)与资源使用(查询)分离 - 缺点是您正在对代码执行的环境添加约束,并且您不必担心重入和并发使用(如果您的连接不是事务性的,则实现会大大简化)。如果您不需要随机访问并且在一般情况下可以不使用 Collection.size(),您可能希望 Cars.all() 返回一个 Iterator 而不是完整的 List。
  • 我明白了。但是我仍然不必担心在上下文中调用 enter() 和 exit() 吗?我的意思是在某种意义上我会在同一个地方同样调用 connection.open() 和 connection.close()?
【解决方案2】:

您可能想看看fluentinterfaces(有一个有趣的例子here)及其“Builder”模式。

你会这样查询:

cars().in(DB).where(id().isEqualTo(1234));

例如,这样你可以在最外层的cars()方法中隐藏连接/断开代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多