自动打开和关闭连接

时间:2010-08-23 15:54:07

标签: java design-patterns

注意:请忽略我使用MultivaluedMap而不是多个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) - 这是面向公众的方法。

以下是Queriable接口:

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. 我不确定那里是否有更好的模式或框架。
  4. 我使用的连接不支持/实现JDBC api,也不是基于sql的。

2 个答案:

答案 0 :(得分:1)

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

基本上归结为:

  1. 定义连接可用的“上下文” (通常是Web应用程序中的请求)
  2. 进入/退出上下文时处理(可能是懒惰)连接的初始化和释放
  3. 将您的方法视为理所当然,只会在这样的背景下使用
  4. 实现并不困难(将连接存储在静态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中最好的方法。

答案 1 :(得分:0)

您可能需要查看fluent interfaces(带有一个有趣的示例here)及其“构建器”模式。

你会这样查询:

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

这样,您可以隐藏最外面的cars()方法中的连接/断开代码。