2015-12-28 4 views
1

Я ищу способ вызова нескольких функций DAO в транзакции, но я НЕ использую весну или какую-либо такую ​​структуру. Фактически у нас есть тип API базы данных .jar, который инициализируется используемым источником данных. То, что я хочу достичь, это иметь свой код бизнес-логики на уровне сделать что-то вроде:Как вызвать несколько функций DAO в транзакции

Connection conn = datasource.getConnection(); 
conn.setAutoCommit(false); 
DAOObject1.query1(params, conn); 
DAOObject2.query4(params, conn); 
conn.commit(); 
conn.setAutoCommit(false); 

однако я хочу, чтобы избежать передачи объекта соединения в каждой отдельной функции, так как это не правильный способ сделать это. Сейчас в нескольких транзакциях мы используем это, но мы ищем способ остановить передачу объекта соединения на уровень базы данных или даже создать его за его пределами. Я ищу что-то вроде:

//Pseudocode 
try{ 
    Datasource.startTransactionLogic(); 
    DAO1.query(params); 
    DAO2.query(params); 
    Datasource.endAndCommitTransactionLogic(); 
} 
catch(SQLException e){ 
    Datasource.rollbackTransaction(); 
} 

Могу ли я достичь этого через EJB? Прямо сейчас мы не используем DAO через инъекцию, мы создаем их вручную, но мы собираемся перейти на EJB и начать использовать их через контейнер. Я слышал, что все запросы, выполняемые EJB, являются транзакционными, но как он знает, к чему откатиться? Через savepoints?

EDIT:

Позвольте мне указать на то, что метод каждого DAO объекта, прямо сейчас, получает свой собственный объект подключения. Вот пример того, как наши DAO классов будут:

public class DAO { 
public DTO exampleQueryMethod(Integer id) { 
    DTO object = null; 
    String sql = "SELECT * FROM TABLE_1 WHERE ID = ?"; 
    try (
     Connection connection = datasourceObject.getConnection(); 
     PreparedStatement statement = connection.prepareStatement(sql) 
    ) { 
     statement.setInt(1, id); 
     try (ResultSet resultSet = statement.executeQuery()) { 
      if (resultSet.next()) { 
       object = DAO.map(resultSet); 
      } 
     } 
    } 
    return object; 
} 
} 

Прямо сейчас, что мы делаем для методов, которые должны быть в транзакции, чтобы иметь вторую копию из них, которые получают Connection объекта:

public void exampleUpdateMethod(DTO object, Connection connection) { 
    //table update logic 
} 

То, что мы хотим, чтобы избежать таких методов в нашей «API базы данных» .jar, но вместо того, чтобы быть в состоянии определить начало и совершать сделки в нашей бизнес-логике, как упоминался в псевдокоде выше.

+5

С EJB (или весной) у вас будет нечто гораздо более простое, безопасное и многоразовое, чем то, что вы желаете. Нет необходимости запускать, совершать и откатывать транзакцию по коду. Если исключение из среды выполнения выбрано из транзакционного метода, транзакция будет автоматически отменена. –

+0

Да, но я ищу способ реализовать транзакционную логику. Могу ли я просто добавить '@ Transactional' в функцию, которая вызывает несколько методов DAO и перезапустить все изменения базы данных при исключении из среды выполнения? В качестве примера у меня есть метод, который создает объект, а затем записывает информацию о двух разных таблицах (всего 3 метода DAO). Если третий метод выбрасывает исключение во время выполнения, я хочу отменить все, даже создание объекта. – Konstantine

+0

Да, это именно то, что делают транзакционные методы EJB и Spring. –

ответ

1

@G. У Демекки есть правильная идея, но я последовал за другой реализацией. Interceptors не смог решить проблему (по крайней мере, из того, что я видел), потому что они должны быть привязаны к каждой функции, которая должна их использовать. Также, когда перехватчик подключен, вызов функции всегда будет перехвачен, что не является моей целью. Я хотел бы иметь возможность явно определять начало и конец транзакции и каждый sql, выполняемый между этими двумя операторами, является частью транзакции SAME, не имея доступа к связанным объектам базы данных (например, к соединению, транзакции и т. Д.) Посредством аргумента прохождение.Как мне удалось достичь этого (и довольно элегантный на мой взгляд) является следующее:

Я создал ConnectionWrapper объект как так:

@RequestScoped 
public class ConnectionWrapper { 

@Resource(lookup = "java:/MyDBName") 
private DataSource dataSource; 

private Connection connection; 

@PostConstruct 
public void init() throws SQLException { 
    this.connection = dataSource.getConnection(); 
} 

@PreDestroy 
public void destroy() throws SQLException { 
    this.connection.close(); 
} 

public void begin() throws SQLException { 
    this.connection.setAutoCommit(false); 
} 

public void commit() throws SQLException { 
    this.connection.commit(); 
    this.connection.setAutoCommit(true); 
} 

public void rollback() throws SQLException { 
    this.connection.rollback(); 
    this.connection.setAutoCommit(true); 
} 

public Connection getConnection() { 
    return connection; 
} 
} 

Мои DAO объекты сами по этой схеме:

@RequestScoped 
public class DAOObject implements Serializable { 

private Logger LOG = Logger.getLogger(getClass().getName()); 

@Inject 
private ConnectionWrapper wrapper; 

private Connection connection; 

@PostConstruct 
public void init() { 
    connection = wrapper.getConnection(); 
} 

public void query(DTOObject dto) throws SQLException { 
    String sql = "INSERT INTO DTO_TABLE VALUES (?)"; 
    try (PreparedStatement statement = connection.prepareStatement(sql)) { 
     statement.setString(1, dto.getName()); 
     statement.executeUpdate(); 
    } 
} 
} 

Теперь я могу легко иметь jax-rs ресурс, который @Inject S этих объектов и начинается и совершает сделки, без необходимости передавать любые Connection или UserTransaction вокруг.

@Path("test") 
@RequestScoped 
public class TestResource { 

@Inject 
ConnectionWrapper wrapper; 

@Inject 
DAOObject dao; 

@Inject 
DAOObject2 dao2; 

@GET 
@Produces(MediaType.TEXT_PLAIN) 
public Response testMethod() throws Exception { 
    try { 
     wrapper.begin(); 
     DTOObject dto = new DTOObject(); 
     dto.setName("Name_1"); 
     dao.query(dto); 
     DTOObject2 dto2 = new DTOObject2(); 
     dto2.setName("Name_2"); 
     dao2.query2(dto2); 
     wrapper.commit(); 
    } catch (SQLException e) { 
     wrapper.rollback(); 
    } 
    return Response.ok("ALL OK").build(); 
} 
} 

И все работает отлично. Нет Interceptors или озираясь InvocationContext и т.д.

Есть только две вещи, меня беспокоит:

  1. Я еще не нашел способ иметь динамическое имя JNDI на @Resource(lookup = "java:/MyDBName"), и это меня беспокоит. В нашем AppServer мы определили много источников данных, и тот, который используется приложением, динамически выбирается в соответствии с файлом ресурсов .xml, упакованным с войной. Это означает, что я не могу знать источник данных JNDI во время компиляции. Существует решение получить источник данных через переменную среды InitialContext(), но мне бы хотелось получить его как ресурс с сервера. Я мог бы также создать производителя @Produces и вводить его таким образом, но все же.
  2. Я не совсем уверен, почему ConnectionWrapper's @PostConstruct получил название ДО DAOObject@PostConstruct. Это правильное и желаемое поведение, но я не понял почему. Я предполагаю, что с DAOObject@Inject s a ConnectionWrapper, его @PostConstruct имеет приоритет, так как он должен быть закончен до того, как DAOObjects может даже начать, но это всего лишь предположение.
1

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

Затем я повесить все вызовы бизнес-уровня как методы из этого объекта репозитория для удобства вызывающего.

Таким образом .. Вы можете позвонить, перемешайте, любые вызовы и использовать базовое соединение, выполнить откат, совершает .. и т.д.

Repository myr = new Repository(datasource); // let constructor create connection 
myr.setAutoCommit(false); 
myr.DAOObject1(parms); // method wrapper 
myr.DAOObject2(parms); // method wrapper 

myr.commitwork(); // method in Repository that calles endAndCommitTransactionLogic 

Затем мы взяли этот новый объект, и создали пул их загрунтовали и управляли в новом потоке, а приложение просто запросило новый «репозиторий» из пула .. и мы пошли.

1

@JBNizet комментарий был прав, но ... подумайте дважды, действительно ли вам нужно перейти на EJB. Даже транзакции там не слишком интуитивно понятны: упаковка вашего исключения в javax.ejb.EJBException не является ни гибкой, ни удобочитаемой. Не говоря уже о других проблемах, например время запуска или интеграционное тестирование.

Судя по вашему вопросу, кажется, что все, что вам нужно, это Dependency Injection рамки с поддержкой для перехватчики. Так возможные пути пойти:

  • Spring, безусловно, самым популярным в этой области
  • CDI (Weld или OpenWebBeans), которые пришли с момента Java EE 6 выпуска - но может использоваться полностью без Java EE Application Server (I используя этот подход прямо сейчас - и он хорошо работает).
  • Guice также поставляется с аннотацией com.google.inject.persist.Transactional.

Все три вышеупомянутых рамок одинаково хороши для случая использования, но существуют и другие факторы, которые следует учитывать, как:

  • который один вы & ваша команда знакома с кривой
  • обучения
  • будущие возможные потребности вашего приложения размер сообщества
  • Framework в
  • тока де Рама прекрасно Скорость velopment
  • т.д.

Надеется, что это поможет вам немного.

EDIT: уточнить ваши сомнения:
Вы можете создать свой собственный Transaction класс, который будет обернуть Connection скачиваются из datasource.getConnection(). Такая транзакция должна быть компонентом CDI CDI и содержать методы метода, такие как begin(), commit() и rollback() - которые назовут connection.commit/rollback под капотом. Затем вы можете написать простой перехватчик, такой как this one, который будет использовать указанную транзакцию и начать/зафиксировать/отменить ее там, где это необходимо (конечно, с AutoCommit отключено).

Это выполнимо, но имейте в виду, что это должно быть тщательно разработанный. Вот почему перехватчики транзакций уже были предоставлены почти на каждой платформе/платформе DI.

+0

Наше приложение уже готово, и мы разрабатываем/расширяем его на стороне (плохая практика, но я не босс). Мы не можем мигрировать из без рамки в Spring. Также у нас есть мобильная версия, разработанная другой командой (которая использует EJB и JPA). Прямо сейчас наша цель - упаковать слой базы данных OUR (веб-приложения) в общую библиотеку для всех сред.Это означает, что мобильная команда будет переноситься из JPA в пользовательскую реализацию БД. Однако для этого нам необходимо решить вышеуказанные проблемы. Кажется, что CDI - это путь, и проще всего перейти от EJB. – Konstantine

+0

Если вы уже используете сервер Java EE 6+, возможно, да. –

+0

Я действительно не вижу, как я мог бы использовать перехватчики для достижения того, что искал ... Как инициировать транзакцию, а затем вызывать функции DAO, а затем совершать/откатывать результаты? Помните, что DAO-компоненты имеют доступ только к общему источнику данных, но не к общему объекту соединения. – Konstantine

Смежные вопросы