|
Java форум JavaTalks форум программистов
|
|
|
|
| Предыдущая тема :: Следующая тема |
| Автор |
Сообщение |
shoo : 115 Новичок
|
Янв 26, 2012 14:48 |
|
|
| Делаю jdbcTemplate.update(), а затем, на основе KeyHolder, делаю jdbcTemplate.update другой таблицы. Всё в одном методе. Мне нужно сделать так, чтобы если что-то вызвало ошибку, то метод бы совсем ничего не делал с БД. Как это сделать? |
|
|
|
 |
shoo : 115 Новичок
|
Янв 26, 2012 15:23 |
|
|
Указываю перед методом
| Код: |
| @Transactional(propagation = Propagation.REQUIRED) |
Но никакой реакции. После первого update у меня вызывается исключение, но он всё-равно записывает в таблицу данные. |
|
|
|
 |
tender_swallow : 472 Бывалый Откуда: Ivanovo
|
Янв 26, 2012 15:47 |
|
|
А вы менеджер транзакци для JDBC настроили?
| Код: |
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean
|
И так как Вы декларативно управляете транзакциями через аннотации, то нужно указать такой элемент к контексте <tx:annotation-driven>. |
|
|
|
 |
shoo : 115 Новичок
|
Янв 26, 2012 16:35 |
|
|
Ага, лол. Я нашёл это в документации и вожусь с этим почти час.
<tx:annotation-driven> не хочет подключаться по умолчанию. Похоже, он требует библиотеки.
Я подключил:
| Код: |
lib\aopalliance.jar
lib\asm-4.0.jar
lib\cglib-2.1_3.jar
|
Но только это не очень помогло. Постоянно вылетает:
java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer
Странно потому, что я попробовал и 2.2 и 2.2.2 cglib. Не знаете, случайно, какие точно библиотеки нужны для подключения tx? |
|
|
|
 |
tender_swallow : 472 Бывалый Откуда: Ivanovo
|
Янв 26, 2012 16:50 |
|
|
|
|
|
|
 |
shoo : 115 Новичок
|
Янв 26, 2012 18:56 |
|
|
В итоге, с asm-3.3 и cglib-2.2 всё заработало. Точнее, аннотация воспринялась, но вот транзакции так и не стали работать. Впрочем, у меня не было времени особенно посмотреть в чём дело, так что завтра продолжу.
Значит, алгоритм такой:
Подключаем <tx:annotation-driven/>
Необходимому методу указываем @Transactional(propagation = Propagation.REQUIRED)
Если метод работает с JdbcTemplate, то все запросы работают в районе одной транзакции, так?
Возможно, у меня происходит запись в БД по той причине, что между запросами посылается Exception и, соответственно, выполнив первый запрос, блок try прерывается и второй запрос просто не создаётся. Поэтому, Spring решает, что всё ОК, т.к. закончилось всё не ошибкой работы с БД и, соответственно коммитит транзакцию.
Надо бы получше изучить документацию. |
|
|
|
 |
mesier : 693 Постоянный посетитель Откуда: Новокузнецк
|
Янв 27, 2012 6:16 |
|
|
| shoo писал(а): |
Значит, алгоритм такой:
Подключаем <tx:annotation-driven/>
Необходимому методу указываем @Transactional(propagation = Propagation.REQUIRED)
Если метод работает с JdbcTemplate, то все запросы работают в районе одной транзакции, так? |
Ну да, так должно быть..
| shoo писал(а): |
| Возможно, у меня происходит запись в БД по той причине, что между запросами посылается Exception и, соответственно, выполнив первый запрос, блок try прерывается и второй запрос просто не создаётся. Поэтому, Spring решает, что всё ОК, т.к. закончилось всё не ошибкой работы с БД и, соответственно коммитит транзакцию. Надо бы получше изучить документацию. |
А какой эксепшн?
Есть у меня подозрения, что транзакции будут откатываться только при выбросе SqlException..
А других вообще-то эксепшенов в методе ДАО и не должно бы быть по идее. ДАО на то он и ДАО, что обязан только принеси-подай между прогой и источником данных выполнять. Если там что-то другое, да ещё небезопасное, это ошибка проектирования..
Логику пре- и пост-обработки резалтсета следует помещать в сервис-классы.
Ваш Кэп.
 |
|
|
|
 |
shoo : 115 Новичок
|
Янв 27, 2012 9:01 |
|
|
У меня всё гораздо хуже ._.
Нет никаких ДАО классов, а всё работает через сервис. Контроллеру нужна информация из БД - он обратился к сервису и получил инфу. Ну ещё POJO есть, для хранения информации и всё. По крайней мере, начинать с такого было проще.
Сейчас попробовал добавить SQLException, ни чем не помогло. Всё так же, одна запись добавляется, а другая - нет. Попробовал "сломать" второй запрос. Тоже ничего.
Блин. |
|
|
|
 |
shoo : 115 Новичок
|
Янв 27, 2012 9:58 |
|
|
Попытался разобраться с ДАО. Вот что получилось:
У меня есть таблица messages и таблица pictures. Соответственно, каждому сообщению по изображению. Хранятся в разных таблицах только потому, что далее хочу сделать несколько изображений получается на сообщение.
В итоге:
а) Делаю модель Message, в котором есть id, author, text, prev_id, picture_id.
б) Делаю на каждую таблицу различные ДАО реализации.
в) В сервисе использую обе ДАО реализации для заполнения модели Message, т.е., например:
| Код: |
added_message_id = MessageDAOImpl.add("John","Hellow, world!", 0);
PictureDAOImpl.add(added_message.id); // return picture id
// то есть, добавляется новое сообщение и картинка, выдаётся id, чтобы кинуть изображение затем в статическую папку под выданным номером.
|
Так как мы помечаем методы DAO как @Transactional, то получится, что в одной транзакции выполняется:
MessageDAOImpl.add("John","Hellow, world!", 0);
В другой:
PictureDAOImpl.add(added_message.id);
Всё правильно описал? |
|
|
|
 |
shoo : 115 Новичок
|
Янв 27, 2012 15:25 |
|
|
| Ура! Сделал всё с DAO и нормально работает. |
|
|
|
 |
mesier : 693 Постоянный посетитель Откуда: Новокузнецк
|
Янв 27, 2012 15:55 |
|
|
| shoo писал(а): |
| Ура! Сделал всё с DAO и нормально работает. |
И как же сделал? |
|
|
|
 |
shoo : 115 Новичок
|
Янв 27, 2012 17:43 |
|
|
| mesier писал(а): |
И как же сделал? |
Вот такое вот чудовище получилось:
| Код: |
public class Message {
private Integer id;
private Date date;
private String author;
private String text;
private Integer prev_id;
// get n set
} |
| Код: |
public class Picture {
private Integer id;
private Integer message_id;
// get n set
} |
| Код: |
@Repository("messageDAOImpl")
public class MessageDAOImpl implements MessageDAO {
private JdbcTemplate jdbcTemplate;
@Resource(name="testDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Transactional
@Override
public Integer create(String author, String text, Integer prevId) {
Message addedMessage = new Message();
addedMessage.setAuthor(author);
addedMessage.setText(text);
addedMessage.setPrev_id(prevId);
final Message finalMessage = addedMessage;
final String sql = "INSERT INTO `messages` (`author`, `text`, `prev_id`) VALUES (?,?,?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection cnctn) throws SQLException {
PreparedStatement ps = cnctn.prepareStatement(
sql,
new String[] {"id"});
ps.setString(1, finalMessage.getAuthor());
ps.setString(2, finalMessage.getText());
ps.setInt(3, finalMessage.getPrev_id());
return ps;
}
}
, keyHolder);
return keyHolder.getKey().intValue();
}
} |
| Код: |
@Repository("pictureDAOImpl")
public class PictureDAOImpl implements PictureDAO{
private JdbcTemplate jdbcTemplate;
@Resource(name="testDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Transactional
@Override
public Integer create(Integer message_id) {
final String sql_picture = "INSERT INTO `pictures` (`message_id`) VALUES (?)";
final Integer finalMessageId = message_id;
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection cnctn) throws SQLException {
PreparedStatement ps = cnctn.prepareStatement(
sql_picture,
new String[] {"id"});
ps.setInt(1, finalMessageId);
return ps;
}
}
, keyHolder);
return keyHolder.getKey().intValue();
}
@Override
public Integer get(Integer id) {
throw new UnsupportedOperationException("Not supported yet.");
}
} |
| Код: |
@Service("messageService")
public class MessageService {
@Resource(name = "messageDAOImpl")
private MessageDAO messageDAO;
@Resource(name = "pictureDAOImpl")
private PictureDAO pictureDAO;
@Transactional(propagation = Propagation.REQUIRED)
public void newMessage(String author, String text, Integer prevId) {
try {
System.out.println(messageDAO.create(author, text, prevId));
System.out.println(pictureDAO.create(0));
} catch(Exception e) {
System.out.println("Error.");
}
}
} |
|
|
|
|
 |
shoo : 115 Новичок
|
Янв 27, 2012 17:49 |
|
|
Мне интересно, правильно ли я понял суть DAO и сервисов. Подскажите?
Правда, метод newMessage сервиса ещё не доработан и 0 там выставлен, чтобы проверить транзакцию(такого сообщения не существует, а таблицы связанны при помощи foreign key).
Я делал из расчёта на то, что интерфейс DAO описывает действия с БД для отдельно взятой таблицы, а точнее такие действия как create, read, update, delete. Реализация DAO реализует работу этого интерфейса, в данном случае при помощи JdbcTemplate. Сервис уже получает то, что нужно контроллеру при помощи методов DAO, попутно реагируя на исключения. |
|
|
|
 |
shoo : 115 Новичок
|
Янв 28, 2012 10:45 |
|
|
Вот, кстати, что мне ответили на SF форуме по этому вопросу:
| Цитата: |
| For starters you ode is flawed NEVER catch the exception and swallow. The transaction management needs to see the exception else rollbacks don't happen, the same for the PreparedStatementCreator (which I wonder why are you using that ?!) |
|
|
|
|
 |
Vermut : 1062 Завсегдатай Откуда: Ростов-на-Дону
|
Янв 29, 2012 10:15 |
|
|
Ты можешь убрать аннотацию Transactional с методов ДАО, потому что транзакция уже открывается при вызове метода сервиса. _________________ Познакомлюсь с привлекательной Ростовчанкой для совместного изучения Java |
|
|
|
 |
|
|
|