Java

Hibernate: Ошибка при сохранении объекта NonUniqueObjectException

NonUniqueObjectException: a different object with the same identifier value was already associated with the session

Данная ошибка проявляется в момент вызова saveOrUpdate() у hibernate template. Суть проблемы в том, что в один момент времени у нас есть два разных объекта с одинаковым идентификатором: один в сессии, а второй тот, что мы пытаемся сохранить.

Person p1 = (Person)getHibernateTemplate().get(Person.class, 1);

...

// здесь мы хотим сохранить Person с именем Alex
// и id = 1 (при том, что у нас уже есть объект p1 в сессии)
Person p2 = new Person();
p2.setId(1);
p2.setName("Alex");

hibernateTemplate.saveOrUpdate(p2); // NonUniqueObjectException

Взаимодействие между потоками в Java. Wait и notify.

Представим ситуацию, что у нас многопотоковое приложение, написанное на Java. Есть некий класс, выполняющий какое-либо конкретное действие с данными, например, отправку их по почте, а сами данные подготавливаются в другом месте кода в другом потоке. Перед отправкой данных нам необходимо как-то связаться с потоком, подготавливающим данные, дабы поймать момент, когда они будут готовы.

Пример 1. Простой, но неправильный пример

public class DataManager {
    private static boolean ready = false;

    public void sendData() {
        while (!ready) {
            // waiting
            System.out.println("Waiting for data...");
        }

        // continue execution and sending data
        System.out.println("Sending data...");
    }

    public void prepareData() {
        System.out.println("Data prepared");
        ready = true;
    }
}

Регулярно выражаемся в Java

Регулярные выражения в Java? А почему бы и нет, как говорится. Время от времени неизбежно появляется необходимость их использовать, чтобы разбить строку на подстроки или, например, отформатировать данные. Сделать это можно несколькими разными способами.

Способ первый, он же самый простой, для несложных манипуляций - использовать встроенные методы класса String для работы с регулярными выражениями.

Пример 1.1. Разбивание строки на массив строк.

String data = "192.168.1.1; 127.0.0.1; 169.254.165.1";
String[] res = data.split(";\\s*");

В результате мы получим массив из трех строк: res[0] = 192.168.1.1, res[1] = 127.0.0.1, res[2] = 169.254.165.1.

Пишем свои equals() и hashCode() методы

Каждый java-разработчик рано или поздно сталкивается с необходимостью переопределения метода equals, а соответственно и hashCode(). Кто-то пишет их сам, кто-то использует прибамбасы среды разработки. Например, я частенько использовал автоматическую генерацию в среде IDE Intellij IDEA (ALT + Insert / equals() and hashCode()), пока не нашел "красивое" решение, реализованное в библиотеке org.apache.commons - HashCodeBuilder() и EqualsBuilder().

Конечно, кто-то скажет, зачем использовать какие-то дополнительные библиотеки и классы, когда все можно самому написать. Ну, что сказать, на вкус и цвет...

Lazy Init или принцип Open session in view

В самом начале своего нелегкого пути по изучению Hibernate и фреймворка Spring для Java столкнулся с проблемой возникновения некого LazyInitException, когда пытался получить некий объект, ссылку на инстанс которого содержал другой, полученный от хибернейта.

Как всегда, горел очередной проект и читать документации особо было некогда, поэтому, пробежавшись быстро по поисковикам, уловил основной смысл данного поведения - при описания меппинга одного объекта по-умолчанию все его связи с другими объектами воспринимаются Hibernate как "ленивые". Иными словами, при наполнении данными объекта Group, который, к примеру, содержит в себе List users и связан с таблицей USER как One-To-Many, Hibernate не пойдет по этой связи наполнять данными список. Сделает он это лишь тогда, когда мы обратимся к этому списку. Здесь то и кроется корень проблемы - в моем приложении обращение к списку шло в тот момент, когда хибернейт сессия работы с базой, в которой я получил объект Group была уже закрыта. Отсюда и LazyInitException.

Наследование в Hibernate

Есть несколько вариантов организации иерархии при работе с Hibernate ORM. На данный момент выделяют три подхода:

  • One table per concrete class - для каждого наследника создается отдельная таблица со всеми полями, включая пронаследованные.
  • One table per class hierarchy - создаем одну "родительскую" таблицу, каждая строка которой должна содержать все поля родительского класса, плюс поля всех наследников, т.е. потенциально кучу null значений.
  • One table per subclass - мэппим базовый класс и наследников на отдельные таблицы и создаем иерархию с помощью внешних ключей.

Остановимся на 3м варианте - One table per subclass, наиболее удачном на мой взгляд, т.к. в базе данных не будет никакой избыточности полей, как в первом варианте, и не будет проблемы масштабирования, как в варианте 2.

Настройка связки Tomcat + Apache

Итак, дано: Apache Http Server и Apache Tomcat. Задача - настроить связку между серверами, дабы можно было легко совмещать Java Web-приложения с другими веб-сервисами, работающими под httpd. В своем примере я использовал Apache 2 и Tomcat 6.

Начнем пожалуй.
1. Необходимо подключить к Apache модуль mod_jk, который будет являться связующим звеном между нашими серверами. Для этого загружаем mod_jk.so подходящей версии, кладем его в каталог с модулями Апача и в /etc/httpd/conf/httpd.conf прописываем строчки:

LoadModule jk_module modules/mod_jk.so

Форматирование чисел с помощью JSTL

1. Форматирование чисел в денежных форматах:

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<fmt:formatNumber value="${account.balance}" type="currency" currencySymbol="&euro;" />

2. Форматирование по шаблону

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<fmt:formatNumber value="12" pattern="###.##"/>  <!-- выдаст 12 -->
<fmt:formatNumber value="12.00" pattern="###.##"/>  <!-- выдаст 12 -->
<fmt:formatNumber value="12.011" pattern="###.##"/>  <!-- выдаст 12.01 -->
<fmt:formatNumber value="12.0066" pattern="###.##"/>  <!-- выдаст 12.01 -->
<fmt:formatNumber value="12.999" pattern="###.##"/>  <!-- выдаст 13 -->
<fmt:formatNumber value="12.1" pattern="###.00"/>  <!-- выдаст 12.10 -->

<fmt:formatNumber value="12" maxFractionDigits="5" minFractionDigits="2"/>   <!-- выдаст 12.00 -->
<fmt:formatNumber value="12.123456789" maxFractionDigits="5" minFractionDigits="2"/>   <!-- выдаст 12.12345 -->

Форматирование даты с помощью JSTL

Простой пример, как можно отформатировать дату на jsp странице с помощью fmt taglib.

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

<fmt:formatDate value="${record.eventDate}" pattern="MMMM d, yyyy, H:mm:ss"/>

Anti-Patterns или как делать не надо

Давно собирался собрать коллекцию анти-паттернов, дабы самому просвятиться и другим помочь. Данную заметку буду периодически пополнять, по мере нахождения последних.

Итак, азы! :)

Конкатенация строк

String s = "";
for (Item i : items) {
    s += ", " + i.getText();
}

Во-первых, конкатенация строк дело плохое, т.к. при этом образуется новый объект строки. Во-вторых, конкатенация в цикле приведет к тому, что для получения единственной конечной строки образуется items.length2 новых мусорных объектов.
Вместо конкатенации необходимо использовать StringBuffer (или даже лучше StringBuilder, если не нужна "thread safe"). При этом, выражения типа stringBuffer.append("val=" + value); естественно приведут к полной бесполезности использования StringBuffer'а.