Показаны сообщения с ярлыком Hibernate. Показать все сообщения
Показаны сообщения с ярлыком Hibernate. Показать все сообщения

понедельник, 20 февраля 2012 г.

Hibernate Proxy

Есть много нюансов с прокси объектами. Кроме того что они протухают, их equals метод может удивить, т.к. отличается от equals клёвого объекта. Так, он по умолчанию сравнивает все поля клёвого объекта, что может привести к лэйзиинициалиэйшн и тп.

Вот тут в тему, рядом.
http://blog.xebia.com/2008/03/08/advanced-hibernate-proxy-pitfalls/

P.S. Hibernate.initialize(..) инициализирует сам прокси, но не меняет его equals, так же как не инициализирует его lazy поля.

среда, 10 августа 2011 г.

SOAPFaultException на платформах .NET и Java, опыт работы с JAX-WS

На днях поимел опыт работы с вебсервисами из коробки-моробки JAX-WS.


1. Все исключения выбрасываемые эндпоинтом нужно делать кастомными и аннотировать
@WebFault(name = "CRUDSoapException", targetNamespace = "wsdl.xxx")
см. блог Eben Hewitt 

Иначе на клиента вываливается SOAPFaultException с кускокм строкового говна, который как бы описывает иксэпшн.

2. Все исключения  выбрасываемые эндпоинтом приходят на платформу .NET в виде строкового
 говна (обёрнутого в SOAPFaultException) в не зависимости от способа объявления на эндпоинте.

3.JAX-B может зажевать исключения возникающие при сериализации, и чтобы таки их увидеть нужно включить такой параметр в запуск программы  -Djavax.net.debug=all

4. JAX-B не умеет сериализовать прокси объекты Hibernate (да и вообще - наверное любые сгенерённые прокси). Бороть  так converting-hibernate-proxy-to-real-object
5. JAX-B не умеет сериализовать интерфейсы.  Если есть необходимость использовать интерфейс можно сделать абстрактный класс реализующий этот интерфейс и обвесить его JAX-B аннотациями(@XmlSeeAlso - с указанием классов). В дальнейшем в коде можно будет использовать этот интерфейс добавив для него частный @XmlJavaTypeAdapter, который будет тупо преобразовывать абстрактный класс в интерфейс. 




вторник, 29 июня 2010 г.

Hibernate and primitive type values

  Примитивные примитивы слишком примитивны для хибернэйта. Парадигму мозгоёбства можно описать следующей фразой одного эксперта:Hibernate doesn't care about values, just mappings(https://forum.hibernate.org/viewtopic.php?t=949814).Сказал в 2004 году - как отрезал. Ничего пока принципиально не поменялось. Геморой проистекает из того, что хибернэйт по-умолчнию вместо примитивных типов пишет в базу null. А потом приводит этот null к примитивам(например int). Так что вылетающие из недр хибернэйта
org.springframework.orm.hibernate3.HibernateSystemException: Null value was assigned to a property of primitive type setter of  MovableObject.movingStatus; nested exception is org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of MovableObject.movingStatus
-"это нормально".

Вообще хибернэйт не обладает magic свойствами, всё что он гарантирует в плане метаданных-
а)При начале работы с чистой бд все мапинги хибернэйта будут промаплены
б)При изменениее мапинга старые данные не будут утеряны

А это означает что изменение мапинга столбцов, таблиц и тп, на уже существующую бд через хибернэйт - будут "как то" применены. "Как-то" - по факту это добавление новых столбцов в таблицу, и сохранение метаданных старых.

Лечение. 

1) 100% лечение. Переписывем все геттеры. вместо примитивных типов используем врапперы (Integer) и делаем их проверку на null

    @Column(name = "OBJECTSTATUS")
    private Integer objectStatus ;


    public int getObjectStatus() {
        if (objectStatus == null) {
            objectStatus = 0;
        }
        return objectStatus;
    }

2) @Column(name = "OBJECTSTATUS", nullable = false)
Лечение работает при вставках новых объектов с незаданными полями, имеет ряд противопоказаний. При добавлении мапинга нового столбца лечение не сработает. Также оно вероятно не сработает если изменить мапинг уже существующей колонки(c firebird 2.1.3+hibernate 3.5.1 не сработает точно).
  
Так же присутствует полный просос при работе с наследованием по @DiscriminatorValue - поля которые казалось бы не используются в объектной модели, очень даже используются в таблицах и как результат - ошибки хибернэйта при попытках кастовать  нулл к примитивам.

3)@Column(name = "OBJECTSTATUS")
    private int objectStatus = 0;
Лечение работает при вставках новых объектов с незаданными полями 

   

Самым правильным наверное будет делать так:

  @Column(name = "OBJECTSTATUS")
    private Integer objectStatus=0;

    public int getObjectStatus() {
        if (objectStatus == null) {
            objectStatus = 0;
        }
        return objectStatus;
    }

-при вставках строк гарантируем что будет выставлен везде 0
-при вставке столбца  гарантируем что не возникнет ошибки и через геттер всегда получим дефолтное значение



PS.
1)прокачать знания про то как 6.2.1. Hibernate event-based validation 2)@NotFound(action=NotFoundAction.IGNORE) - это для объектов

среда, 19 мая 2010 г.

Hibernate+Collections+N*Thread = ConcurrentModificationException

При сохранении ентитей, содержащих коллекции из нескольких трэдов, возможно ConcurrentModificationException. Вне зависимости от секций синхронизации(с.с.), присутствующих внутри транзакции( типа TransactionTemplate tt = new TransactionTemplate(entityManager.getTransactionManager());), итерации по колециям выполняются вне с.с. при сохранении в базу. Чтобы гарантировать отсутствие ConcurrentModificationException необходимо включить в секцию синхронизации всю транзакцию.

четверг, 15 апреля 2010 г.

org.firebirdsql.jdbc.FBSQLException: GDS Exception. 335544336. deadlock update conflicts with concurrent update

Эта хрень возникает из за того что два потока пытаются одновременно изменить поля одной и той же ентити. Вот что пишут про это
 http://www.firebirdfaq.org/faq151/ ,
http://edn.embarcadero.com/article/30213.

В целом есть два способа это побороть

1)В коде добавить синхронизацию

2)Разобраться с изоляцией транзакций.
Подробнее:
http://ru.wikipedia.org/wiki/Уровни_изолированности_транзакций
для хибернэйта они задаются параметром  hibernate.connection.isolation в пропертях,
значения описаны в java.sql.Connection:
Наверняка:
для hibernate, в пропертях прописать: hibernate.connection.isolation=8

Выдержки из java.sql.Connection:

    /**
     * A constant indicating that transactions are not supported.
     */
    int TRANSACTION_NONE     = 0;

    /**
     * A constant indicating that
     * dirty reads, non-repeatable reads and phantom reads can occur.
     * This level allows a row changed by one transaction to be read
     * by another transaction before any changes in that row have been
     * committed (a "dirty read").  If any of the changes are rolled back,
     * the second transaction will have retrieved an invalid row.
     */
    int TRANSACTION_READ_UNCOMMITTED = 1;

    /**
     * A constant indicating that
     * dirty reads are prevented; non-repeatable reads and phantom
     * reads can occur.  This level only prohibits a transaction
     * from reading a row with uncommitted changes in it.
     */
    int TRANSACTION_READ_COMMITTED   = 2;

    /**
     * A constant indicating that
     * dirty reads and non-repeatable reads are prevented; phantom
     * reads can occur.  This level prohibits a transaction from
     * reading a row with uncommitted changes in it, and it also
     * prohibits the situation where one transaction reads a row,
     * a second transaction alters the row, and the first transaction
     * rereads the row, getting different values the second time
     * (a "non-repeatable read").
     */
    int TRANSACTION_REPEATABLE_READ  = 4;

    /**
     * A constant indicating that
     * dirty reads, non-repeatable reads and phantom reads are prevented.
     * This level includes the prohibitions in
     * TRANSACTION_REPEATABLE_READ and further prohibits the
     * situation where one transaction reads all rows that satisfy
     * a WHERE condition, a second transaction inserts a row that
     * satisfies that WHERE condition, and the first transaction
     * rereads for the same condition, retrieving the additional
     * "phantom" row in the second read.
     */
    int TRANSACTION_SERIALIZABLE     = 8;

среда, 17 февраля 2010 г.

HibernateOptimisticLockingFailureException

При попытке сделать hibernateTemplate.saveOrUpdate(entity) вывалился такой иксэпшн:


org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

Иксэпшн вываливается потому что у entity был id!=null и в базе не было entity с такой id. Фактически хибернэйт при сохранении entity вместо insert пытался сделать update. 

Решил проблему вызов save вместо saveOrUpdate.

Помогла

вторник, 6 октября 2009 г.

org.hibernate.AssertionFailure: possible nonthreadsafe access to session

Следствие того что где-то в коде присутствует следующая последовательность

          hibernate.saveOrUpdate(obj);
          ................................................................
          hibernate.evict(obj);
причём эта последовательность находится внутри транзакции(если нет транзакции то между этой последовательностью нету flush())

четверг, 24 сентября 2009 г.

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

   Ошибка возникает потому что пытаемся сохранить/удалить/проапдейтить объект из другой сессии, который уже загружен в нашей сессии(закэширован).
 Лечение:
1) 100% лекарство использовать hibernateTemplate.merge(object);
2) 100% лекарство которое приводит к обнулению всего кэша

      hibernateTemplate.flush();
      hibernateTemplate.clear();
      hibernateTemplate.saveOrUpdate(object);


3)   лекарство может не помочь

      hibernateTemplate.evict(object); - убирает объекты из кэша сравнивая их по объектной ссылке
      hibernateTemplate.saveOrUpdate(object); - проверяет наличие объектов в кэше(прежде чем их сохранить) по айдишнику. Вывод: такая пара смысла не имеет(ну, имеет смысл в рамках одной сессии и совсем для другого).

      Можно написать так:
      Class dataObjectClass = object.getClass();
      long id = object.getId();
      T dataObject = (T) hibernateTemplate.get(dataObjectClass, id);

      if (dataObject != null) {
        hibernateTemplate.evict(dataObject );
      }

Но это спасёт нас только в том случае - если в ентитях нигде нету перекрёстных ссылок. Если они есть - то при каскадном сохранении будут ентити добавляться в кэш по нескольку раз и в результате получим тот же иксэпшн(Зы: иксэпшн будет валиться непосредственно при комите транзакции и всякие flush перед evict тут непомогут).

    
  
4) делать всё в одной сессии
5)загрузить по id,  выставить необходимые поля, сохранить

org.hibernate.LazyInitializationException

Возникает потому что сессия, в которой был получен объект - закрыта, а поля объекта были помечены как @XXXToXXX(fetch = FetchType.LAZY).
Лечение:
1)меняем аннотацию на @XXXToXXX(fetch =FetchType.EAGER) - самый простой способ но вызывает осложнения типа долгой загрузки объекта
2)вычитав объект впервые тут же инициализируем поле с FetchType.LAZY -
делаем это так - hibernateTemplate.initialize(entity) - самый бестолковый способ
3)даём новой сессии права на работу с объектом -    
if (!hibernateTemplate.contains(track)) {
                hibernateTemplate.lock(track, LockMode.READ);;
 - самый клёвый способ НО!
you only use lock() if you are sure that the object has not been modified - иначе 

org.springframework.dao.InvalidDataAccessApiUsageException: cannot lock an unsaved transient instance: 
4) делать всё в одной сессии
5)прикрепляем к новой сессии через
             if (!hibernateTemplate.contains(track)) {
                 hibernateTemplate.merge(track);}
6)если таки точно собрались загружать поля то можно сделать hql запрос типа:
List<Customer> list = hibernateTemplate.find(
            "select distinct с from Customer с " +
            "left join fetch c.orders where c=?", customer);
в sql
 select distinct from Customer c left join fetch c.orders
 http://bwinterberg.blogspot.com/2009/08/how-to-eagerly-fetch-associations-with.html
7)Есть спец фильтры для загрузки нужных частей сущностей см. тут
http://bwinterberg.blogspot.com/2009/09/hibernate-preload-pattern.html
8)держать сессию открытой(паттерн типа '...а после нас хоть потоп') http://alekseiko.blogspot.com/2010/02/hibernate-lazyinitializationexception.html
9)загрузить по id,  выставить необходимые поля, сохранить
10)Написать свою аннотацию, которая будет проверять необходимость переподключения коллекции к новой сессии: http://9mmedia.com/blog/?p=272