Статьи

Hibernate Performance Tips: эффект грязной коллекции

После 8 лет разработки серверных и встраиваемых приложений с использованием Hibernate в качестве ORM , изо всех сил пытаясь найти решения для повышения производительности Hibernate , чтения блогов и участия в конференциях, я решил поделиться с вами этими знаниями, приобретенными за эти годы.

Это первый пост из множества других постов:

В прошлом году я посетил Devoxx в качестве докладчика, но также я посетил конференцию Patrycja Wegrzynowicz, посвященную анти- паттернам гибернации . В этой презентации Патрича показывает нам антишаблон, который шокирует меня, потому что он оказался неожиданным.

Мы увидим, какой эффект это имеет, когда Hibernate обнаруживает грязную коллекцию и должен воссоздать ее.

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

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
@Entity
public class Starship {
 
 private Long id;
 @Id @GeneratedValue(strategy=GenerationType.SEQUENCE) public Long getId() {return id;}
 public void setId(Long id) {this.id = id;}
 
 private Date launched;
 @Temporal(TemporalType.DATE)  public Date getLaunched() {return launched;}
 public void setLaunched(Date launched) {this.launched = launched;}
 
 private String registry;
 @Column(unique=true, nullable=false) public String getRegistry() {return registry;}
 public void setRegistry(String registry) {this.registry = registry;}
 
 private StarshipClassEnum starshipClassEnum;
 @Enumerated public StarshipClassEnum getStarshipClassEnum() {return starshipClassEnum;}
 public void setStarshipClassEnum(StarshipClassEnum starshipClassEnum) {this.starshipClassEnum = starshipClassEnum;}
 
 
 private AffiliationEnum affiliationEnum;
 @Enumerated public AffiliationEnum getAffiliationEnum() {return affiliationEnum;}
 public void setAffiliationEnum(AffiliationEnum affiliationEnum) {this.affiliationEnum = affiliationEnum;}
 
 
 private Physics physics;
 @Embedded public Physics getPhysics() {return physics;}
 public void setPhysics(Physics physics) {this.physics = physics;}
 
 private List<Officer> officers = new ArrayList<Officer>();
 @OneToMany(cascade={CascadeType.ALL}) public List<Officer> getOfficers() {return Collections.unmodifiableList(officers);}
 protected void setOfficers(List<Officer> officers) {this.officers = officers;}
 public void addOfficer(Officer officer) {
  officer.setStarship(this);
  this.officers.add(officer);
 }
 
 public Starship() {
  super();
 }
 
 public Starship(String registry) {
  setRegistry(registry);
 }
 
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((registry == null) ? 0 : registry.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Starship other = (Starship) obj;
  if (registry == null) {
   if (other.registry != null)
    return false;
  } else if (!registry.equals(other.registry))
   return false;
  return true;
 }
}
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
@Entity
public class Officer {
 
 private Long id;
 @Id @GeneratedValue(strategy=GenerationType.SEQUENCE)public Long getId() {return id;}
 protected void setId(Long id) {this.id = id;}
 
 
 private String name;
 @Column(unique=true, nullable=false) public String getName() {return this.name;}
 public void setName(String name) {this.name = name;}
 
 
 private SpeciesEnum speciesEnum;
 @Enumerated public SpeciesEnum getSpeciesEnum() {return speciesEnum;}
 public void setSpeciesEnum(SpeciesEnum speciesEnum) {this.speciesEnum = speciesEnum;}
 
 
 private PlanetEnum homePlanet;
 @Enumerated public PlanetEnum getHomePlanet() {return homePlanet;}
 public void setHomePlanet(PlanetEnum homePlanet) {this.homePlanet = homePlanet;}
 
 
 private AffiliationEnum affiliationEnum;
 @Enumerated public AffiliationEnum getAffiliationEnum() {return affiliationEnum;}
 public void setAffiliationEnum(AffiliationEnum affiliationEnum) {this.affiliationEnum = affiliationEnum;}
 
 
 private RankEnum rank;
 @Enumerated @NotNull public RankEnum getRank() {return rank;}
 public void setRank(RankEnum rank) {this.rank = rank;}
 
 private Starship starship;
 @ManyToOne public Starship getStarship() {return starship;}
 protected void setStarship(Starship starship) {this.starship = starship;}
 
 public Officer() {
  super();
 }
 
 public Officer(String name, RankEnum rank) {
  setName(name);
  setRank(rank);
 }
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Officer other = (Officer) obj;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }
 
}

На предыдущих занятиях мы должны обратить внимание на три важных момента:

  • мы комментируем на уровне свойств, а не на уровне полей.
  • @ OneToMany и @ ManyToOne используют параметры по умолчанию (кроме определения каскада )
  • офицер- геттер в классе Starship возвращает неизменный список.

Чтобы протестировать конфигурацию модели, мы собираемся создать тест, который создает и сохраняет один звездный корабль и семь офицеров , а в разных транзакциях и EntityManager находит созданный звездный корабль .

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class StarshipPersistenceTests {
 
 @Inject
 private EntityManagerFactory entityManagerFactory;
 
 @Test
 public void testSaveOrderWithItems() throws Exception {
 
  Starship starship = createData();
  findStarship(starship);
 
 }
 
 private Starship createData() {
  EntityManager entityManager = entityManagerFactory.createEntityManager();
  EntityTransaction transaction = entityManager.getTransaction();
  transaction.begin();
 
  Physics physics = physics().height(137.5D).length(642.5D)
    .power("Wrap reactor").width(467.0D).build();
 
  Calendar launched = Calendar.getInstance();
  launched.set(2363, 9, 4);
 
  Starship starship = starship().registry("NCC-1701-D").physics(physics)
    .launched(launched.getTime())
    .starshipClass(StarshipClassEnum.GALAXY)
    .affiliation(AffiliationEnum.STARFLEET).build();
 
  Officer jeanLucPicard = officer().name("Jean-Luc Picard")
    .rank(RankEnum.CAPTAIN).affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.EARTH).speciment(SpeciesEnum.HUMAN)
    .build();
  starship.addOfficer(jeanLucPicard);
 
  Officer williamRiker = officer().name("William Riker")
    .rank(RankEnum.COMMANDER)
    .affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.EARTH).speciment(SpeciesEnum.HUMAN)
    .build();
  starship.addOfficer(williamRiker);
 
  Officer data = officer().name("Data")
    .rank(RankEnum.LIEUTENANT_COMMANDER)
    .affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.OMICRON_THETA)
    .speciment(SpeciesEnum.ANDROID).build();
  starship.addOfficer(data);
 
  Officer geordiLaForge = officer().name("Geordi La Forge")
    .rank(RankEnum.LIEUTENANT)
    .affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.EARTH).speciment(SpeciesEnum.HUMAN)
    .build();
  starship.addOfficer(geordiLaForge);
 
  Officer worf = officer().name("Worf").rank(RankEnum.LIEUTENANT)
    .affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.QONOS).speciment(SpeciesEnum.KLINGON)
    .build();
  starship.addOfficer(worf);
 
  Officer beverlyCrusher = officer().name("Beverly Crusher")
    .rank(RankEnum.COMMANDER)
    .affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.EARTH).speciment(SpeciesEnum.HUMAN)
    .build();
  starship.addOfficer(beverlyCrusher);
 
  Officer deannaTroi = officer().name("Deanna Troi")
    .rank(RankEnum.COMMANDER)
    .affiliation(AffiliationEnum.STARFLEET)
    .homePlanet(PlanetEnum.BETAZED).speciment(SpeciesEnum.BETAZOID)
    .build();
  starship.addOfficer(deannaTroi);
 
  entityManager.persist(starship);
 
  transaction.commit();
  entityManager.close();
  return starship;
 }
 
 private void findStarship(Starship starship) {
 
  EntityManager entityManager = this.entityManagerFactory.createEntityManager();
  EntityTransaction transaction = entityManager.getTransaction();
  transaction.begin();
  System.out.println("Before Find");
  Starship newStarship = entityManager.find(Starship.class, starship.getId());
  System.out.println("After Find Before Commit");
  transaction.commit();
  System.out.println("After commit");
  entityManager.close();
 
 }
}

Теперь, когда мы создали этот тест, мы можем запустить его и будем наблюдать за выводом консоли Hibernate .

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
Hibernate: insert into Starship (affiliationEnum, launched, height, length, power, width, registry, starshipClassEnum, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
 
Before Find Starship By Id
 
Hibernate: select starship0_.id as id1_0_, starship0_.affiliationEnum as affiliat2_1_0_, starship0_.launched as launched1_0_, starship0_.height as height1_0_, starship0_.length as length1_0_, starship0_.power as power1_0_, starship0_.width as width1_0_, starship0_.registry as registry1_0_, starship0_.starshipClassEnum as starship9_1_0_ from Starship starship0_ where starship0_.id=?
 
After Find Starship By Id and Before Commit
 
Hibernate: select officers0_.Starship_id as Starship1_1_2_, officers0_.officers_id as officers2_2_, officer1_.id as id0_0_, officer1_.affiliationEnum as affiliat2_0_0_, officer1_.homePlanet as homePlanet0_0_, officer1_.name as name0_0_, officer1_.rank as rank0_0_, officer1_.speciesEnum as speciesE6_0_0_, officer1_.starship_id as starship7_0_0_, starship2_.id as id1_1_, starship2_.affiliationEnum as affiliat2_1_1_, starship2_.launched as launched1_1_, starship2_.height as height1_1_, starship2_.length as length1_1_, starship2_.power as power1_1_, starship2_.width as width1_1_, starship2_.registry as registry1_1_, starship2_.starshipClassEnum as starship9_1_1_ from Starship_Officer officers0_ inner join Officer officer1_ on officers0_.officers_id=officer1_.id left outer join Starship starship2_ on officer1_.starship_id=starship2_.id where officers0_.Starship_id=?
Hibernate: delete from Starship_Officer where Starship_id=?
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
Hibernate: insert into Starship_Officer (Starship_id, officers_id) values (?, ?)
 
After commit

Посмотрите количество запросов, выполненных во время первого коммита (сохраняющиеся объекты) и во время коммита второй транзакции (поиск звездолета ). Всего и игнорируя генератор последовательностей, мы можем считать 22 вставки , 2 выбора и 1 удаление , что неплохо, когда мы создаем только 8 объектов и 1 находим по первичному ключу.

На этом этапе давайте рассмотрим, почему выполняются эти SQL- запросы:

Первые восемь вставок неизбежны; они требуются путем вставки данных в базу данных.

Следующие семь вставок необходимы, потому что мы аннотировали свойство getOfficers без атрибута mappedBy . Если мы внимательно посмотрим на документацию Hibernate , то мы увидим, что « без описания какого-либо физического сопоставления используется однонаправленное соединение со многими с таблицей соединений ».

Следующая группа запросов еще более странная, первое предложение select — найти Starship по id, но что это за удаления и вставки данных, которые мы уже создали?

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

Поскольку используется таблица соединения, таблицу Starship_Officer следует создать заново, удалив предыдущие вставленные кортежи и вставив новые (хотя они имеют одинаковые значения).

Попробуем исправить эту проблему. Мы начнем с отображения двунаправленной ассоциации «один ко многим», где сторона «многие к одному» является стороной-владельцем.

1
2
3
4
private List<Officer> officers = new ArrayList<Officer>();
@OneToMany(mappedBy="starship", cascade={CascadeType.ALL}) public  List<Officer> getOfficers() {return Collections.unmodifiableList(officers);}
protected void setOfficers(List<Officer> officers) {this.officers = officers;}
public void addOfficer(Officer officer) {this.officers.add(officer);}

И теперь мы снова запускаем тот же тест и снова проверяем результат.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
Hibernate: insert into Starship (affiliationEnum, launched, height, length, power, width, registry, starshipClassEnum, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
 
Before Find Starship By Id
 
Hibernate: select starship0_.id as id1_0_, starship0_.affiliationEnum as affiliat2_1_0_, starship0_.launched as launched1_0_, starship0_.height as height1_0_, starship0_.length as length1_0_, starship0_.power as power1_0_, starship0_.width as width1_0_, starship0_.registry as registry1_0_, starship0_.starshipClassEnum as starship9_1_0_ from Starship starship0_ where starship0_.id=?
 
After Find Starship By Id and Before Commit
 
Hibernate: select officers0_.starship_id as starship7_1_1_, officers0_.id as id1_, officers0_.id as id0_0_, officers0_.affiliationEnum as affiliat2_0_0_, officers0_.homePlanet as homePlanet0_0_, officers0_.name as name0_0_, officers0_.rank as rank0_0_, officers0_.speciesEnum as speciesE6_0_0_, officers0_.starship_id as starship7_0_0_ from Officer officers0_ where officers0_.starship_id=?
 
After commit

Несмотря на то, что мы сократили количество операторов SQL с 25 до 10, у нас все еще есть ненужный запрос, который находится только в секции commit второй транзакции. Почему, если офицеры по умолчанию ленивы (спецификация JPA ), а мы не получаем офицеров в транзакции, Hibernate выполняет выбор из таблицы «Сотрудники»? По той же причине, что и в предыдущей конфигурации, возвращаемая коллекция имеет другой идентификатор Java, поэтому Hibernate помечает его как вновь созданную коллекцию, но теперь очевидно, что операции объединения таблиц больше не требуются. Мы сократили количество запросов, но у нас все еще есть проблемы с производительностью. Вполне вероятно, что нам понадобится какое-то другое решение, и решение не самое очевидное, мы не собираемся возвращать объекты коллекции, возвращенные Hibernate , мы могли бы расширить это позже, но мы собираемся изменить расположение аннотаций.

То, что мы собираемся сделать, — это изменить местоположение отображения с подхода со свойством на использование отображения поля. Просто мы собираемся переместить все аннотации к атрибутам класса, а не к получателям .

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
@Entity
public class Starship {
 
 @Id @GeneratedValue(strategy=GenerationType.SEQUENCE)
 private Long id;
 public Long getId() {return id;}
 protected void setId(Long id) {this.id = id;}
 
 @Temporal(TemporalType.DATE) private Date launched;
 public Date getLaunched() {return launched;}
 public void setLaunched(Date launched) {this.launched = launched;}
 
        ...
 
        @OneToMany(mappedBy="starship", cascade={CascadeType.ALL})
 private List<Officer> officers = new ArrayList<Officer>();
 public List<Officer> getOfficers() {return Collections.unmodifiableList(officers);}
 protected void setOfficers(List<Officer> officers) {this.officers = officers;}
 public void addOfficer(Officer officer) {
  officer.setStarship(this);
  this.officers.add(officer);
 }
 
 public Starship() {
  super();
 }
 
        public Starship(String registry) {
  setRegistry(registry);
 }
 
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((registry == null) ? 0 : registry.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Starship other = (Starship) obj;
  if (registry == null) {
   if (other.registry != null)
    return false;
  } else if (!registry.equals(other.registry))
   return false;
  return true;
 }
}
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
@Entity
public class Officer {
 
 @Id @GeneratedValue(strategy=GenerationType.SEQUENCE) private Long id;
 public Long getId() {return id;}
 protected void setId(Long id) {this.id = id;}
 
 
 @Column(unique=true, nullable=false) private String name;
 public String getName() {return this.name;}
 public void setName(String name) {this.name = name;}
 
 
 @Enumerated private SpeciesEnum speciesEnum;
 public SpeciesEnum getSpeciesEnum() {return speciesEnum;}
 public void setSpeciesEnum(SpeciesEnum speciesEnum) {this.speciesEnum = speciesEnum;}
 
        ...
 
 
        @ManyToOne private Starship starship;
 public Starship getStarship() {return starship;}
 protected void setStarship(Starship starship) {this.starship = starship;}
 
 public Officer() {
  super();
 }
 
 public Officer(String name, RankEnum rank) {
  setName(name);
  setRank(rank);
 }
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Officer other = (Officer) obj;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }
}

И, наконец, мы снова запустим тест и посмотрим, что произойдет:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
Hibernate: insert into Starship (affiliationEnum, launched, height, length, power, width, registry, starshipClassEnum, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into Officer (affiliationEnum, homePlanet, name, rank, speciesEnum, starship_id, id) values (?, ?, ?, ?, ?, ?, ?)
 
Before Find
Hibernate: select starship0_.id as id1_0_, starship0_.affiliationEnum as affiliat2_1_0_, starship0_.launched as launched1_0_, starship0_.height as height1_0_, starship0_.length as length1_0_, starship0_.power as power1_0_, starship0_.width as width1_0_, starship0_.registry as registry1_0_, starship0_.starshipClassEnum as starship9_1_0_ from Starship starship0_ where starship0_.id=?
 
After Find Before Commit
After commit

Почему при использовании сопоставления свойств Hibernate выполняет запросы во время фиксации, а при использовании сопоставления полей не выполняется? Когда транзакция фиксируется, Hibernate выполняет сброс для синхронизации основного постоянного хранилища с постоянным состоянием, хранящимся в памяти. Когда используется сопоставление свойств, Hibernate вызывает методы getter / setter для синхронизации данных, а в случае метода getOfficers возвращает грязную коллекцию (из-за вызова unmodifiableList ). С другой стороны, когда мы используем сопоставление полей, Hibernate получает непосредственно поле, поэтому сбор не считается грязным и повторное создание не требуется.

Но мы еще не закончили, я полагаю, вам интересно, почему мы не удалили Collections.unmodifiableList из геттера, возвращая коллекцию Hibernate? Да, я согласен с вами, что мы быстро закончили, и изменение выглядело бы как @ OneToMany (cascade = {CascadeType.ALL} ) public List <Officer> getOfficers () {фицеры; } но возвращение исходной коллекции приводит к проблеме инкапсуляции, фактически мы нарушаем инкапсуляцию !. Мы можем добавить в изменяемый список все, что захотим; мы могли бы применить неконтролируемые изменения к внутреннему состоянию объекта.

Использование unmodifiableList — это подход, позволяющий избежать нарушения инкапсуляции, но, конечно, мы могли бы использовать разные средства доступа для общего доступа и доступа в спящем режиме, не вызывая метод Collections.unmodifiableList .

Учитывая то, что мы видели сегодня, я предлагаю вам всегда использовать аннотации полей вместо сопоставления свойств, мы избавим вас от множества неожиданностей.

Надеюсь, вы нашли этот пост полезным.

Скриншот примера, показанного здесь: