代码之家  ›  专栏  ›  技术社区  ›  Andriy Drozdyuk Pickels

自动打开和关闭连接

  •  2
  • Andriy Drozdyuk Pickels  · 技术社区  · 15 年前

    注意:请忽略我对 MultivaluedMap 而不是多个变量 字符串…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
    

    我目前的做法是用一个抽象类, 可提取的 调用抽象方法 查询(字符串…参数) 查询(int id) ,任何扩展它的类都必须实现。

    抽象可实现 可查询的 接口,使其公开三个公共方法 筛选器(字符串…参数) , () 获取(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);   
    }
    

    下面是实现它的可抽象类:

    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();
    }
    

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

    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。

    2 回复  |  直到 15 年前
        1
  •  1
  •   giorgiga    15 年前

    你可以用名人的变种 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中,它通常包含每个请求的处理过程),从主方法,如果你想在应用程序的整个生命周期中使用一个连接来编写一个简单的案例,到API中最好的方法。

        2
  •  0
  •   Community CDub    8 年前

    你可能想看看 fluent interfaces (举个有趣的例子 here )以及它的“构建者”模式。

    您可以这样查询:

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

    例如,通过这种方式,您可以在最外面的cars()方法中隐藏连接/断开连接代码。