вторник, 27 января 2009 г.

Транзакции и ORACLE LOB вручную в spring

Недавно столкнулся с необходимостью сохранения LOB-объектов в базу Oracle из Spring приложения. Проблема была в записи кусков более 32k (Меньшие в CLOB пишутся через String, без мучений).
Пример использования транзакций без аннотаций:


Application Context:

<bean id="reportDao" class="***">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="lobHandler" ref="${reportDao.lobHandler}"/>
</bean>

<!-- Transaction manager for DataSource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- LobHandler for Oracle JDBC drivers -->
<!-- (refers to the NativeJdbcExtractor above to get access to native OracleConnections) -->

<bean id="oracleLobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">
<property name="nativeJdbcExtractor" ref="nativeJdbcExtractor"/>
</bean>


DAO implements:

private DataSourceTransactionManager transactionManager;
public void setReport(int numRep, String code) {
TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
MyTransaction transaction = new MyTransaction(numRep, code);
try {
txTemplate.execute(transaction);
} catch (Exception e) {
log.warn("TRANSACTION EXCEPTION! " +e, e);
}
}

private class MyTransaction extends TransactionCallbackWithoutResult {
/** Logger for this class and subclasses */
protected final Log log = LogFactory.getLog(getClass());

private String clobData;
private int numRep;

public MyTransaction(int numRep, String clobData) {
this.clobData = clobData;
this.numRep = numRep;
}

public void doInTransactionWithoutResult(TransactionStatus status) {
JdbcTemplate jdbcTemplate = getJdbcTemplate();

Integer id = numRep; //obtain ID for new record
CLOB clob = null;

try {
clob = CLOB.empty_lob();

jdbcTemplate.update(
"update Report set report_text=? where id = ? ",
new Object[] { clob, id },
new int[] { Types.CLOB, Types.INTEGER });

List packetList = jdbcTemplate.query(
"select report_text from Report where id=? for update",
new Object[]{ id },
new int[] { Types.INTEGER },
new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getClob("report_text");
}
});

if (packetList.size() == 1) {
Writer os = ((CLOB)packetList.get(0)).getCharacterOutputStream();
os.write(clobData);
os.flush();
os.close();
} else {
//handle - this shouldn't happen
}
} catch (SQLException e) {
log.warn(e,e);
} catch (IOException e) {
log.warn(e,e);
}
}
}

Всё просто.
Увы, не помню линк, окуда вытащил пример.

понедельник, 26 января 2009 г.

Подсказки

Часто у меня спрашивают вопросы по какой-нибудь технологии. Я частенько даю книжку или посылаю искать в инете или читать документацию.
На меня обижаются, что вместо того чтобы подсказать, я посылаю.. Ну и пусть, зато знаний прибавиться. Смысл отвечать на элементарные вопросы? Пусть помучаются, поищут, зато знаний прибавится и запомнят лучше, чем на халяву подскажу что-нить.
Я стараюсь объяснять только конкретно по своему коду или логику работы куска кода, которую проще и внятнее объяснить, чем заставлять разбираться в коде или часто (увы) криво написанной документации.

"Размазаная" логика.

Недавно неделю убил на один глюк. История поучительная. В общем передали мне приложение - на сокетах, сервер плюс клиент написанное на java. Сервер управляет логикой и посылает/принимает сигналы с клиента. Плюс имеется диспетчерское приложение на делфе, которое я не трогаю (ну и слава богу ;)). Связь не ахти какая, да и просто доступность клиентов не гарантируется. Вот.
Ну и очень грубо описание... Требовалось обеспечить выполнение длительных процессов, автоматических, управляемых сервером, в которых с клиентов только выбирали задания, а потом получали уточнения о ходе выполнения. Для блокировок заданий между сеансами клиентов, используется таблица со звучным названием "block". В неё записывается id клиента, и тип блокировки (грубое представление).
Я всё это протестил, кое где потом поставили, вроде работало. (Система не единая, разбита на полностью независимые группы). И в одной такой группе повалились глюки. Задания не выбирались в автоматическом режиме.
Перешерстили весь код клиента и сервера, по ходу исправили пару узких мест (это плюс), но никак не могли понять в чём дело. У нас работает, у заказчика нет, причём видно, что нарушается логика обработки заданий. В тех логах, что имелись, было не ясно, что происходит. В БД нет возможности отследить состояние на момент возникновения глюка и эмулировать проблему.
Постепенно, частично уже от отчаянья, вышли на уровень проверки всех программ. И, в итоге, выяснилось, что Делфийская программа, при определённых условиях "вычищала" блокировки клиентов и логика заданий просто рушилась. Причём, чистка зависела от настроек и пр.
Комментировать не буду. В общем учитесь на чужих ошибках.

четверг, 22 января 2009 г.

Сегодня день моей специальности (22.01).

Всех поздравляю. Учился на системотехника (22.01) - работаю java-программистом..
Постоянно забываю про эту дату, но, к счастью, находятся добрые люди, напоминают!
Ну и немного скучноватое пожелание: всем с моей специальности найти достойную и интересную работу!