Статьи

Учебное пособие по JPA: составление карт объектов — часть 3

В моей последней статье я показал два разных способа чтения / записи постоянного состояния сущности — поле и свойство. Когда используется режим доступа к полю, JPA непосредственно считывает значения состояния из полей объекта, используя отражение. Он напрямую переводит имена полей в имена столбцов базы данных, если мы не указываем имена столбцов явно. В случае режима доступа к свойству методы getter / setter используются для чтения / записи значений состояния. В этом случае мы аннотируем методы получения состояний сущностей вместо полей, используя те же аннотации. Если мы не указываем явно имена столбцов базы данных, то они определяются в соответствии с соглашением JavaBean, то есть путем удаления части «get» из имени метода-получателя и преобразования первой буквы оставшейся части имени метода в символ нижнего регистра.

Мы можем указать, какой режим доступа использовать для сущности, используя аннотацию @Access в объявлении класса сущности. Эта аннотация принимает аргумент типа AccessType (определенный в пакете javax.persistence ), который имеет два разных значения, соответствующих двум разным режимам доступа — FIELD и PROPERTY . Например, мы можем указать режим доступа к свойству для объекта Address следующим образом:

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
@Table(name = "tbl_address")
@Access(AccessType.PROPERTY)
public class Address {
  private Integer id;
  private String street;
  private String city;
  private String province;
  private String country;
  private String postcode;
  private String transientColumn;
 
  @Id
  @GeneratedValue
  @Column(name = "address_id")
  public Integer getId() {
    return id;
  }
 
  public Address setId(Integer id) {
    this.id = id;
    return this;
  }
 
  public String getStreet() {
    return street;
  }
 
  public Address setStreet(String street) {
    this.street = street;
    return this;
  }
 
  public String getCity() {
    return city;
  }
 
  public Address setCity(String city) {
    this.city = city;
    return this;
  }
 
  public String getProvince() {
    return province;
  }
 
  public Address setProvince(String province) {
    this.province = province;
    return this;
  }
 
  public String getCountry() {
    return country;
  }
 
  public Address setCountry(String country) {
    this.country = country;
    return this;
  }
 
  public String getPostcode() {
    return postcode;
  }
 
  public Address setPostcode(String postcode) {
    this.postcode = postcode;
    return this;
  }
}

Несколько замечаний по поводу приведенного выше примера:

  1. Как обсуждалось ранее, теперь мы аннотируем метод получения идентификатора сущности с помощью аннотаций @Id , @GeneratedValue и @Column .
  2. Поскольку теперь имена столбцов будут определяться путем анализа методов получения, нам больше не нужно помечать поле transientColumn аннотацией @Transient . Однако если у объекта Address есть какой-либо другой метод, имя которого начинается с «get», то нам нужно применить к нему @Transient .

Если у объекта нет явной информации о режиме доступа, как и у нашего объекта Address, который мы создали в первой части этой серии , то JPA принимает режим доступа по умолчанию. Это предположение сделано не случайно. Вместо этого JPA сначала пытается выяснить расположение аннотации @Id . Если для поля используется аннотация @Id , то предполагается режим доступа к полю. Если аннотация @Id используется в методе получения, тогда предполагается режим доступа к свойству. Таким образом, даже если мы удалим аннотацию @Access из объекта Address в приведенном выше примере, сопоставление все равно будет действительным, и JPA перейдет в режим доступа к свойству:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@Entity
@Table(name = "tbl_address")
public class Address {
  private Integer id;
  private String street;
  private String city;
  private String province;
  private String country;
  private String postcode;
  private String transientColumn;
 
  @Id
  @GeneratedValue
  @Column(name = "address_id")
  public Integer getId() {
    return id;
  }
 
  // Rest of the class........

Несколько важных моментов, которые следует помнить о режимах доступа:

  1. Никогда не следует объявлять поле общедоступным, если вы используете режим доступа к полю. Все поля сущности должны иметь либо закрытый (лучший!), Защищенный тип доступа, либо тип доступа по умолчанию. Причина этого заключается в том, что объявление полей как открытых позволит любому незащищенному классу иметь прямой доступ к состояниям объекта, что может легко победить реализацию провайдера. Например, предположим, что у вас есть объект, все поля которого общедоступны. Теперь, если этот объект является управляемым объектом (что означает, что он был сохранен в базе данных), и любой другой класс изменяет значение его идентификатора , а затем вы пытаетесь сохранить изменения обратно в базу данных, вы можете столкнуться с непредсказуемым поведением (I постараюсь подробно остановиться на этой теме в следующей статье). Даже сам класс сущности должен манипулировать полями только во время инициализации (т. Е. Внутри конструкторов).
  2. В случае режима доступа к свойству, если мы применим аннотации к методам-установщикам, а не к методам-получателям, они просто будут игнорироваться.

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

  1. Пометьте объект аннотацией @Access и укажите AccessType.FIELD в качестве режима доступа для всех полей.
  2. Отметьте поле, для которого вы не хотите использовать режим доступа к полю, с помощью аннотации @Transient .
  3. Пометьте метод получения свойства аннотацией @Access и укажите AccessType.PROPERTY в качестве режима доступа.

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

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
@Entity
@Table(name = "tbl_address")
@Access(AccessType.FIELD)
public class Address {
  @Id
  @GeneratedValue
  @Column(name = "address_id")
  private Integer id;
 
  private String street;
  private String city;
  private String province;
  private String country;
  
  /**
    * postcode is now marked as Transient
    */
  @Transient
  private String postcode;
  
  @Transient
  private String transientColumn;
 
  public Integer getId() {
    return id;
  }
 
  public Address setId(Integer id) {
    this.id = id;
    return this;
  }
 
  public String getStreet() {
    return street;
  }
 
  public Address setStreet(String street) {
    this.street = street;
    return this;
  }
 
  public String getCity() {
    return city;
  }
 
  public Address setCity(String city) {
    this.city = city;
    return this;
  }
 
  public String getProvince() {
    return province;
  }
 
  public Address setProvince(String province) {
    this.province = province;
    return this;
  }
 
  public String getCountry() {
    return country;
  }
 
  public Address setCountry(String country) {
    this.country = country;
    return this;
  }
 
  /**
    * We are now using property access mode for reading/writing
    * postcode
    */
  @Access(AccessType.PROPERTY)
  public String getPostcode() {
    return postcode;
  }
 
  public Address setPostcode(String postcode) {
    this.postcode = postcode;
    return this;
  }
}

Здесь важно отметить, что если мы не аннотируем класс аннотацией @Access для явного указания режима доступа к полям в качестве режима по умолчанию, и мы аннотируем как поля, так и методы получателя, то результирующее поведение отображения будет неопределенным. Это означает, что результат будет полностью зависеть от поставщика сохраняемости, то есть один поставщик может выбрать режим доступа к полю по умолчанию, другой может использовать режим доступа к свойству или может принять решение об исключении!

Вот и все на сегодня. Если вы обнаружите какие-либо проблемы / у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь комментировать!

До скорого.

Ссылка: JPA Tutorial: Mapping Entities — часть 3 от нашего партнера JCG Саима Ахмеда в блоге Codesod .