#java #hibernate #jpa
#java #переход в спящий режим #jpa
Вопрос:
У меня есть иерархическая система классов для сущностей, и все они в порядке, пока я не введу отношения. Вот несколько примеров кода:
Базовый класс:
@MappedSuperclass
public class BaseClass
{
// Some fields
// Ids are MySQL autoincrementing unsigned BIGINT
protected Long id;
// ... other fields
@JsonCreator
public BaseClass (
// ...fields
)
{
// assign fields
}
public BaseClass () { /** Default copy constructor */ }
public BaseClass update (BaseClass value)
{
// repeat bellow line for all fields
this.field1 = (value.field1 != null) ? value.field1 : this.field1;
// finally, for chaining
return this;
}
// Getters and Setters for all fields
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
@JsonProperty("id")
public Long getId ()
{
return this.id;
}
public BaseClass setId (Long value)
{
this.id = value != null ? value : this.id;
return this;
}
@Column(name = "field1")
@JsonProperty("field1")
public String getField1 ()
{
return this.field1;
}
public BaseClass setField1 (Field1Type value)
{
this.field1 = value;
return this;
}
}
Унаследованный класс:
@Entity
@Table(name = "`table_name`")
@DynamicInsert
@DynamicUpdate
public class InheritedClass extends BaseClass implements Serializable
{
/**
* the value is for demonstration only, it's randomly generated per serializable entity
*/
private static final long serialVersionUID = 1L;
private List<RelatedClass1> relatedClass1Field;
private RelatedClass2 relatedClass2Field;
public InheritedClass () { /** Default copy constructor */ }
public InheritedClass (BaseClass value)
{
super.update(value);
}
// inheritedClassField is the field in the many-to-one end of the relationship in RelatedClass1
@Transient // javax.persistence, not bean
@JsonProperty("related_class1_field")
@OneToMany(
targetEntity=RelatedClass1.class
, fetch=FetchType.EAGER
, cascade = { CascadeType.ALL }
, mappedBy = "inheritedClassField"
)
public List<RelatedClass1> getRelatedClass1Field ()
{
return this.relatedClass1Field;
}
public InheritedClass setRelatedClass1Field (List<RelatedClass1> value)
{
this.relatedClass1Field = value;
return this;
}
// inheritedClassField is the field in the many-to-one end of the relationship in RelatedClass1
@Transient // javax.persistence, not bean
@JsonProperty("related_class2_field")
@ManyToOne(
targetEntity=RelatedClass2.class
, fetch=FetchType.EAGER
, cascade = { CascadeType.ALL }
)
@JoinColumn(
name = "related_id"
, referencedColumnName = "id"
)
public RelatedClass2 getRelatedClass2Field ()
{
return this.relatedClass2Field;
}
public InheritedClass setRelatedClass2Field (RelatedClass2 value)
{
this.relatedClass2Field = value;
return this;
}
}
Когда я пытаюсь получить доступ к экземпляру InheritedClass
, relatedClass1Field
и relatedClass2Field
являются null
, однако они заполняются в базе данных.
НО
Если я определяю связь с помощью стратегии доступа к полю, они возвращают правильное значение:
@Access(AccessType.FIELD)
@OneToMany(
targetEntity=RelatedClass1.class
, fetch=FetchType.EAGER
, cascade = { CascadeType.ALL }
, mappedBy = "inheritedClassField"
)
private List<RelatedClass1> relatedClass1Field;
@ManyToOne(
targetEntity=RelatedClass2.class
, fetch=FetchType.EAGER
, cascade = { CascadeType.ALL }
)
@JoinColumn(
name = "related_id"
, referencedColumnName = "id"
)
private RelatedClass2 relatedClass2Field;
@JsonProperty("related_class1_field")
public List<RelatedClass1> getRelatedClass1Field ()
{
return this.relatedClass1Field;
}
public InheritedClass setRelatedClass1Field (List<RelatedClass1> value)
{
this.relatedClass1Field = value;
return this;
}
@JsonProperty("related_class2_field")
public RelatedClass2 getRelatedClass2Field ()
{
return this.relatedClass2Field;
}
public InheritedClass setRelatedClass2Field (RelatedClass2 value)
{
this.relatedClass2Field = value;
return this;
}
Комментарии:
1. Большинство людей все равно помещают эти аннотации в поле, так есть ли проблема?
2. @K.Nicholas Поскольку я использую наследование сущностей, я использую стратегию доступа к свойствам, чтобы я мог легко переопределить реализацию способа преобразования данных при доступе к данным верхнего уровня, сохраняя при этом доступность полей при доступе к данным более низкого уровня.
3. Я думаю, что если вы попытаетесь добавить аннотации к свойству дочернего класса, которые должны быть применены к родительскому классу, у вас возникнут проблемы с JPA.
4. @Хитрый, пожалуйста, покажите место / места, где вы помещаете
@Id
аннотацию?5. @Хитрый, почему вы используете
@Transient
для полученияInheritedClass
? Вы должны использовать его только в том случае, если хотите исключить поле из постоянного состояния объекта.
Ответ №1:
Вы не должны использовать @Transient
аннотации для этих методов:
@Transient
@OneToMany(
targetEntity=RelatedClass1.class
, fetch=FetchType.EAGER
, cascade = { CascadeType.ALL }
, mappedBy = "inheritedClassField"
)
public List<RelatedClass1> getRelatedClass1Field ()
// ...
@Transient
@ManyToOne(
targetEntity=RelatedClass2.class
, fetch=FetchType.EAGER
, cascade = { CascadeType.ALL }
)
@JoinColumn(
name = "related_id"
, referencedColumnName = "id"
)
public RelatedClass2 getRelatedClass2Field ()
// ...
Согласно аннотации документации @Transient
указывает, что свойство или поле не являются постоянными.
И простой пример. Представьте, что у вас есть следующая сущность:
@Entity
@Table(name = "TST_PATIENT")
public class Person {
private Long id;
// ...
private Date dateOfBirth;
@Id
@Column(name = "P_ID")
public Long getId() {
return id;
}
@Column(name = "P_DOB")
public Date getDateOfBirth() {
return dateOfBirth;
}
@Transient
public long getAge() {
return ChronoUnit.YEARS.between(
LocalDateTime.ofInstant( Instant.ofEpochMilli( dateOfBirth.getTime()), ZoneOffset.UTC),
LocalDateTime.now()
);
}
// setters omitted for brevity
}
Здесь мы используем @Transient
аннотацию, потому что на самом деле у нас нет age
столбца в TST_PATIENT
таблице (он просто вычисляется на основе другого сохраняемого поля), и мы хотим дать указание hibernate исключить это свойство из постоянного состояния сущности.