1.19.2015

7. Создание, чтение, обновление, удаление сущностей. Hibernate



Давайте начнем с простых вещей с создания сущности,  точнее, с сохранения transient  объекта в базе данных, т.е. перевода его в состояние persistent .  Сохраняют сущность в базе данных  методом  .save(),  особенность этого метода в том, что он сразу возвращает первичный ключ сущности и при этом генерирует  sql  команду insert, если  его первичным ключом  является поле для которого используется метод генерации “identity” , а не “sequence”. Используется в рамках одной транзакции.  Смотрим листинг:

         session.getTransaction().begin();
            Person person = new Person();
               
            Long id = (Long) session.save(person);
               
            session.getTransaction().begin();

Так же есть метод .persist() который, как и метод  .save() переводит объект из состояния transient в состояние persistent. В противоположность методу .save(),   .persist() не пытается сразу же вернуть нам  первичный ключ сущности, его применяют, если операции у нас продолжительны по времени, вызов sql  команды  insert  может быть  отложен на момент комита транзакции .commit() или вызова метода  .flush().  Используется обычно для  длительных по времени и сложных  последовательностей операций вне рамках транзакций. Смотрим листинг:

            session = sessionFactory.openSession();
                session.getTransaction().begin();
                Person person = new Person();
              
                session.getTransaction().commit();
               
                session.persist(person);
              //вызов вне рамках транзакции!
               
                session.getTransaction().begin();
                session.flush();
                 session.close();

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

session.save(person);
session.flush();
session.refresh(person); 

где метод .flush() сначала скидывает все изменения в базу данных, а метод  .refresh() обновляет данные после выполнения логики триггеров. И так, если у нас объект сущности связан с сессией,  то он находится в состоянии persistent, как мы только выходим за рамки сессии, сущность переходит в состояние detached. В этом состоянии объект сущности может быть изменен,  например такой метод используется во всех web приложениях.  Для того что бы изменения сделанные вне сессии/транзакции применились  для detached объекта,  нужно обратно перевести его в состояние persistent, это делается методом  .update(). Смотрим листинг:

Long id = (Long) session.save(person);
             
                session.getTransaction().commit();
                session.close();
               
                person.setFirstName("Oleg");
               
                session = sessionFactory.openSession();
                session.getTransaction().begin();
                session.update(person);
               
                session.getTransaction().commit();
                session.close();

Но в этом примере для метода  .update() есть свой подводный камень,  допустим, что у нас в сессии есть объект с первичным ключом сущности,  который уже связан с сессией и такой же ключ имеет наш detached объект. Т. е. если  мы попытаемся выполнить  по нему метод  .update(),  то  мы получим исключительную ситуацию  org.hibernate.NonUniqueObjectException  и так сгенерируем  все это,  добавим строчку кода Person person2 = (Person)session.get(Person.class,id); перед  session.update(person). Смотрим листинг:

Long id = (Long) session.save(person);
             
                session.getTransaction().commit();
                session.close();
               
                person.setFirstName("Oleg");
               
                session = sessionFactory.openSession();
                session.getTransaction().begin();
               Person person2 = (Person)session.get(Person.class,id);
                session.update(person); // получим ошибку org.hibernate.NonUniqueObjectException 
               
                session.getTransaction().commit();
                session.close();

Что бы избежать исключительной ситуации мы должны применить  .merge(),  эта команда подобна команде saveOrUpdateCopy() в Hibernate 2. Она возвращает нам ссылку на копию  новой сущности, при этом берется persistent объект с  таким же ключом как у detached объекта,  все изменения перезаписываются с detached объекта поверх  persistent объекта. После этого мы имеем два объекта,  detached объект и копию ссылки на   persistent объект с копированными значениями полей detached сущности. Далее  с копией мы работаем как с  persistent объектом и все изменения, которые мы хотим сохранить в базе данных, мы применяем к этой копии,  т. е. метод  .merge()  не переводит сущность из detached состояния в состояние persistent как метод  .update(). Рассмотрим все на примере, смотрим на листинг:

               Person person = new Person();
                person.setFirstName("Vitaly");
                person.setSecondName("Lopanov");
                Long id = (Long) session.save(person);             
                session.getTransaction().commit();
                session.close();
                person.setFirstName("Oleg");
                session = sessionFactory.openSession();
                session.getTransaction().begin();
                Person person2 = (Person)session.get(Person.class,id);
                System.out.printf("Person select  person2: %s\n", person2);
                Person person3 = (Person)session.merge(person);
                person.setFirstName("Vika");\\ эти изменения ни когда не попадут в базу данных
                \\ дальше нужно работать с копией person3 как с persistent объектом.
                System.out.printf("Person select  person2: %s\n", person2);
                if (person != person3) {System.out.format("ссылка на новый instance\n");}
                session.flush();
                person = (Person)session.get(Person.class,id);
                System.out.printf("Person select: %s\n", person);
    \\ Вывод: Person select: Person{id=1, firstName='Oleg', secondName='Lopanov'}

Есть еще один интересный метод  .saveOrUpdate() который вобрал в себя поведение  двух методов .save() и .update().  Если нет  объекта,  то метод  .saveOrUpdate() ведет себя как .save() и создает persistent  объект в сессии, иначе он ведет себя как метод .update() обновляет объект. Хотелось бы сказать по поводу применения методов  .save() ,  .update(),  .saveOrUpdate(),  если ваша сущность находиться в состоянии persistent,  то не нужно писать лишнии команды и повторно переводить сущность в состояние persistent, это типично для новичков. Смотрим листинг:

                session = sessionFactory.openSession();
                session.getTransaction().begin();
                Person person = (Person)session.get(Person.class,id); /*  метод  .get()возвращает сущность person которая находится в состоянии persistent и все изменения которые мы предпримем с сущностью, автоматом перепишутся в Базу Данных после выполнения методов  .flush() или .commit() */
                person.setFirstName("Vitaly");
                session.saveOrUpdate(person); /* лишний вызов метода .saveOrUpdate()  типично для новичков  */
                session.flush();

Для удаления сущности нужно выполнить метод  сессии .delete(), сущность будет переведена в состояние removed, а после выполнения методов .flush() или .commit() будет удалена и из таблицы базы данных.  Нужно учитывать одну особенность,  если у сущности есть “дети”  они могут остаться без “родителей”, т.е. сначала нужно удалить  зависимые объекты от этой сущности, потом и саму сущность. Смотрим листинг:

            session = sessionFactory.openSession();
                session.getTransaction().begin();
                Person person = (Person)session.get(Person.class,id);
                session.delete(person);
                session.flush();

Прочитать сущность можно несколькими способами, методы .get() и .load() возвращают нам сущности по заранее известному нам ключу, в чем отличие этих методов я писал выше, не будем заострять на них внимание и повторятся. Для чтения множества “массива” сущностей  можно прибегнуть к двум методам, первый это  вернуть  сразу все записи методом .list(), смотрим листинг:

            List<Person> persons = session.createCriteria(Person.class).list();
                for (Person p: persons ) {
                    System.out.printf("%s\n", p);
               

Второй способ это работать с итератором (Iterator),  его нужно и можно использовать для повышения производительности  запроса данных, если запрос находиться в кэше второго уровня и выборка данных идет из кэша. Если нет данных в кэше, то запрос с итератором будет работать дольше, чем запрос методом  .list(). Смотрим листинг:

                Iterator<Person> iterator = session.createQuery("from Person").iterate();
                while (iterator.hasNext()) {
                    Person p = iterator.next();
                    System.out.printf("%s\n", p);
                }

Постраничная выборка или pagination, мы можем ограничить результаты выборки и обрабатывать всё, малыми порциями в этом нам помогут две команды .setMaxResults(), количество сущностей, сколько вернуть в запросе и начиная с какой сущности .setFirstResult(). Постраничная выборка реализуется обычно внутреннем механизмом базы данных. Например, вывести 5 сущностей, пропустив десять первых сущностей в выборке, смотрим на листинг:

                persons = session.createCriteria(Person.class).setMaxResults(5).setFirstResult(10).list();
                for (Person p: persons ) {
                    System.out.printf("%s\n", p);
                }

Комментариев нет:

Отправить комментарий