Статьи

Hibernate пример с отложенной загрузкой

Этот пост будет посвящен тому, почему и как мы используем концепции, известные как загрузка LAZY и EAGER в приложении, и как использовать спящий шаблон Spring для загрузки наших объектов LAZY способом EAGER.

И, конечно, как следует из названия, мы покажем это на примере. Сценарий как таковой;

Вы родитель, у которого есть ребенок с большим количеством игрушек. Но текущая проблема в том, что когда бы ты ни позвонил ему (мы предполагаем, что у тебя есть мальчик), он приходит к тебе со всеми своими игрушками. Теперь это проблема, так как вы не хотите, чтобы он все время носил свои игрушки.

Таким образом, будучи обоснованным родителем, вы идете вперед и определяете игрушки ребенка как ленивые. Теперь, когда вы звоните ему, он приходит к вам без игрушек.

Но вы столкнулись с другой проблемой. Когда придет время для семейной поездки, вы хотите, чтобы он взял с собой свои игрушки, потому что в противном случае ребенку будет скучно. Но так как вы строго обязали ЛЕНЗИ к игрушке ребенка, вы не можете попросить его взять с собой игрушки. Это где выборка EAGER вступает в игру. Давайте сначала посмотрим на наши доменные классы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.fetchsample.example.domain;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
 
/**
 * Holds information about the child
 *
 * @author dinuka.arseculeratne
 *
 */
@Entity
@Table(name = 'CHILD')
@NamedQuery(name = 'findChildByName', query = 'select DISTINCT(chd) from Child chd left join fetch chd.toyList where chd.childName=:chdName')
public class Child {
 
 public static interface Constants {
  public static final String FIND_CHILD_BY_NAME_QUERY = 'findChildByName';
 
  public static final String CHILD_NAME_PARAM = 'chdName';
 }
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 /**
  * The primary key of the CHILD table
  */
 private Long childId;
 
 @Column(name = 'CHILD_NAME')
 /**
  * The name of the child
  */
 private String childName;
 
 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
 /**
  * The toys the child has. We do not want the child to have the same toy more than
  * once, so we have used a set here.
  */
 private Set<Toy> toyList = new HashSet<Toy>();
 
 public Long getChildId() {
  return childId;
 }
 
 public void setChildId(Long childId) {
  this.childId = childId;
 }
 
 public String getChildName() {
  return childName;
 }
 
 public void setChildName(String childName) {
  this.childName = childName;
 }
 
 public Set<Toy> getToyList() {
  return toyList;
 }
 
 public void addToy(Toy toy) {
  toyList.add(toy);
 }
 
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.fetchsample.example.domain;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
/**
 * Hols data related to the toys a child possess
 *
 * @author dinuka.arseculeratne
 *
 */
@Entity
@Table(name = 'TOYS')
public class Toy {
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 @Column(name = 'TOY_ID')
 /**
  * The primary key of the TOYS table
  */
 private Long toyId;
 
 @Column(name = 'TOY_NAME')
 /**
  * The name of the toy
  */
 private String toyName;
 
 public Long getToyId() {
  return toyId;
 }
 
 public void setToyId(Long toyId) {
  this.toyId = toyId;
 }
 
 public String getToyName() {
  return toyName;
 }
 
 public void setToyName(String toyName) {
  this.toyName = toyName;
 }
 
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((toyName == null) ? 0 : toyName.hashCode());
  return result;
 }
 
 @Override
 /**
  * Overriden because within the child class we use a Set to
  * hold all unique toys
  */
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Toy other = (Toy) obj;
  if (toyName == null) {
   if (other.toyName != null)
    return false;
  } else if (!toyName.equals(other.toyName))
   return false;
  return true;
 }
 
 @Override
 public String toString() {
  return 'Toy [toyId=' + toyId + ', toyName=' + toyName + ']';
 }
 
}

Итак, как вы можете видеть, у нас есть две простые сущности, представляющие ребенка и игрушку. У ребенка есть отношения один-ко-многим с игрушками, что означает, что у одного ребенка может быть много игрушек (о, как я скучаю по своим детским дням). После этого нам нужно взаимодействовать с данными, поэтому давайте продолжим и определим интерфейс и реализацию DAO (объекта доступа к данным).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.fetchsample.example.dao;
 
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
 
import com.fetchsample.example.domain.Child;
 
/**
 * The basic contract for dealing with the {@link Child} entity
 *
 * @author dinuka.arseculeratne
 *
 */
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public interface ChildDAO {
 
 /**
  * This method will create a new instance of a child in the child table
  *
  * @param child
  *            the entity to be persisted
  */
 public void persistChild(Child child);
 
 /**
  * Retrieves a child without his/her toys
  *
  * @param childId
  *            the primary key of the child table
  * @return the child with the ID passed in if found
  */
 public Child getChildByIdWithoutToys(Long childId);
 
 /**
  * Retrieves the child with his/her toys
  *
  * @param childId
  *            the primary key of the child table
  * @return the child with the ID passed in if found
  */
 public Child getChildByIdWithToys(Long childId);
 
 /**
  * Retrieves the child by the name and with his/her toys
  *
  * @param childName
  *            the name of the child
  * @return the child entity that matches the name passed in
  */
 public Child getChildByNameWithToys(String childName);
 
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.fetchsample.example.dao.hibernate;
 
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
 
import com.fetchsample.example.dao.ChildDAO;
import com.fetchsample.example.domain.Child;
 
/**
 * The hibernate implementation of our {@link ChildDAO} interface
 *
 * @author dinuka.arseculeratne
 *
 */
public class ChildDAOHibernateImpl extends HibernateDaoSupport implements
  ChildDAO {
 
 /**
  * {@inheritDoc}
  */
 public void persistChild(Child child) {
  getHibernateTemplate().persist(child);
 }
 
 /**
  * {@inheritDoc}
  */
 public Child getChildByIdWithoutToys(Long childId) {
  return getHibernateTemplate().get(Child.class, childId);
 }
 
 /**
  * {@inheritDoc}
  */
 public Child getChildByIdWithToys(Long childId) {
  Child child = getChildByIdWithoutToys(childId);
  /**
   * Since by default the toys are not loaded, we call the hibernate
   * template's initialize method to populate the toys list of that
   * respective child.
   */
  getHibernateTemplate().initialize(child.getToyList());
  return child;
 }
 
 /**
  * {@inheritDoc}
  */
 public Child getChildByNameWithToys(String childName) {
  return (Child) getHibernateTemplate().findByNamedQueryAndNamedParam(
    Child.Constants.FIND_CHILD_BY_NAME_QUERY,
    Child.Constants.CHILD_NAME_PARAM, childName).get(0);
 
 }
 
}

Простой контракт. У меня есть четыре основных метода. Первый, конечно, просто сохраняет дочернюю сущность в базе данных.

Второй метод извлекает дочерний элемент по переданному первичному ключу, но не выбирает игрушки.

Третий метод сначала извлекает ребенка, а затем извлекает игрушки ребенка, используя метод initialize () шаблона Hibernate. Обратите внимание, что когда вы вызываете метод initialize (), hibernate извлекает вашу коллекцию, определенную LAZY, в дочерний прокси, который вы получили.

Последний метод также извлекает игрушки ребенка, но на этот раз с использованием именованного запроса. Если мы вернемся к Именованному запросу объекта Child, вы увидите, что мы использовали « выборку из левого соединения». Это выборка ключевых слов, которая фактически инициализирует коллекцию игрушек также при возврате объекта Child, который соответствует требованиям. Наконец у меня есть основной класс для тестирования нашей функциональности;

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.fetchsample.example;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.fetchsample.example.dao.ChildDAO;
import com.fetchsample.example.domain.Child;
import com.fetchsample.example.domain.Toy;
 
/**
 * A test class
 *
 * @author dinuka.arseculeratne
 *
 */
public class ChildTest {
 
 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    'com/fetchsample/example/spring-context.xml');
 
  /**
   * First we initialize a child
   */
  Child child = new Child();
 
  /**
   * A cool ben 10 action figure
   */
  Toy ben10 = new Toy();
  ben10.setToyName('Ben 10 figure');
 
  /**
   * A even more cooler spider man action figure
   */
  Toy spiderMan = new Toy();
  spiderMan.setToyName('Spider man figure');
 
  child.setChildName('John');
  /**
   * Add the toys to the collection
   */
  child.addToy(ben10);
  child.addToy(spiderMan);
 
  ChildDAO childDAO = (ChildDAO) context.getBean('childDAO');
 
  childDAO.persistChild(child);
 
  Child childWithoutToys = childDAO.getChildByIdWithoutToys(1L);
  // The following line will throw a lazy initialization error since we have
  // defined fetch type as LAZY in the Child domain class.
  // System.out.println(childWithToys.getToyList().size());
 
  Child childWithToys = childDAO.getChildByIdWithToys(1L);
  System.out.println(childWithToys.getToyList().size());
 
  Child childByNameWithToys = childDAO.getChildByNameWithToys('John');
 
  System.out.println(childByNameWithToys.getToyList().size());
 
 }
}

Определение вашей базовой сущности как LAZY является хорошей практикой, поскольку во многих случаях вы можете не захотеть коллекций внутри сущности, а просто взаимодействовать с данными в вашей базовой сущности. Но в случае, если вам нужны данные ваших коллекций, вы можете использовать любой из методов, упомянутых ранее.

Это об этом на сегодня. Для тех, кто хочет опробовать пример, я загрузил его здесь .

Ссылка: Ленивая / Стремительная загрузка с использованием режима гибернации на примере нашего партнера JCG Динуки Арурилератне в блоге My Journey Through IT .