Actually, @Airy's assumption is absolutely relevant!
I just have modeled the issue with a small h2
-based project. Disclaimer: it's just a piece of code of a synthetic model
Given: Pet
entity. It has a typeCode
property of Integer
.
Todo: Implement another property, that'll return pet's type name by its typeCode
.
Here, we use @Transient
annotation to specify that a given attribute should not be persisted, and @PostLoad
annotation to make initTransient()
method be invoked after entity loading. @Formula
annotation is used to calculate a custom value for a typeName
during entity loading
@Entity
public class Pet {
@Transient
public String ownerLastName;
@PostLoad
void initTransient() {
ownerLastName = Optional.ofNullable(getOwner()).map(Owner::getLastName).orElse("'Doe'");
}
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "r_owner_id", referencedColumnName = "id")
private Owner owner;
@Id
@Column(name = "id", nullable = false)
@GeneratedValue
private Long id;
@Column
private String name;
@Column
private Integer typeCode;
@Formula("case type_code when 1 then 'Cat' when 2 then 'Dog' else 'Chupacabra' end")
private String typeName;
//get, set etc
@Override
public String toString() {
return new StringJoiner(", ", Pet.class.getSimpleName() + "[", "]")
.add("id=" + getId())
.add("name='" + getName() + "'")
.add("typeCode=" + getTypeCode())
.add("ownerLastName=" + getOwnerLastName())
.add("typeName='" + getTypeName() + "'")
.toString();
}
}
In a bootstrap:
var cat = new Pet();
cat.setName("Oscar");
cat.setTypeCode(1);
var dog = new Pet();
dog.setName("Lucky");
dog.setTypeCode(2);
var customPet = new Pet();
customPet.setName("Smoking kills");
Set<Pet> pets = Set.of(cat, dog, customPet);
var owner = new Owner();
owner.setFirstName("John");
owner.setPets(pets);
ownerRepository.save(owner);
System.out.println("Database was successfully initialized with data: ");
System.out.println(cat);
System.out.println(owner);
Console:
Pet[id=1, name='Oscar', age=6, ownerLastName=null, typeCode=1, typeName='null', type=CAT]
Owner[id=1, lastName='null', firstName='John']
As you can see, ownerLastName=null
here, but let's go on and load data.
test:
public void interact() {
var pet1 = petRepository.findById(1L).get();
var pet2 = petRepository.findById(2L).get();
var pet = petRepository.findById(3L).get();
var owner = ownerRepository.findById(1L).get();
System.out.println(pet1);
System.out.println(pet2);
System.out.println(pet);
System.out.println(owner);
}
Console:
Pet[id=1, name='Oscar', age=6, ownerLastName='Doe', typeCode=1, typeName='Cat', type=CAT]
Pet[id=2, name='Lucky', age=10, ownerLastName='Doe', typeCode=2, typeName='Dog', type=DOG]
Pet[id=3, name='Smoking kills', age=null, ownerLastName='Doe', typeCode=null, typeName='Chupacabra', type=null]
Owner[id=1, lastName='null', firstName='John']
As you can see, ownerLastName
has been initialized with a default Doe
value.
From Hibernate
's reference of formula:
You can use a SQL fragment (aka formula) instead of mapping a property
into a column