Автоматически открывать и закрывать соединение - PullRequest
2 голосов
/ 23 августа 2010

ПРИМЕЧАНИЕ. Пожалуйста, игнорируйте мое использование <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) - это общедоступные методы.

Вот интерфейс 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. Я не уверен, есть ли лучший образец или структура там.

Используемое мной соединение не поддерживает / не реализует API JDBC и не основано на sql.

Ответы [ 2 ]

1 голос
/ 23 августа 2010

Вы можете использовать вариацию (в) известного Открытого сеанса в представлении паттерна.

В основном все сводится к этому:

  1. Определите «контекст», в котором доступны соединения (обычно запрос в веб-приложениях)
  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();
}

Этот блок может быть помещен куда угодно (в веб-приложениях он обычно обрабатывает каждый запрос) - из основного метода, если вы кодируете простой случай, когда вы хотите, чтобы одно соединение было доступно на протяжении всего срока службы вашего приложения, на лучшие методы в вашем API.

0 голосов
/ 23 августа 2010

Возможно, вы захотите взглянуть на свободно интерфейсов (с интересным примером здесь ) и его шаблон "Builder".

Вы бы запросили вот так:

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

Таким образом, вы можете скрыть код подключения / отключения в внешнем методе cars (), например.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...