User Tools

Site Tools


hibernate

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
hibernate [2014/11/06 10:20] skipidarhibernate [2023/07/14 12:42] (current) – [Transactional] skipidar
Line 1: Line 1:
 +Hibernate is the implementation of the [[http://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html|Java Persistence API]].
  
 +
 +
 +==== Annotations ====
 +Tutorials: http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/
 +
 +
 +**In examples there is:**
 +{{http://i520.photobucket.com/albums/w327/schajtan/2014-11-06_10-55-15_zps527a98a0.png}}
 +
 +  * One **Department** has many employees
 +  * One **Employee** has one deparment
 +
 +
 +**ACHTUNG:**
 +  * Bei **One**ToMany, **Many**ToOne bezieht sich der erste Teil auf die Klasse, in welcher die annotierte Variable befindet. \\ Wenn wir in der Klasse **Department** das Set **employees** annotieren - dann steht dort //@OneToMany//. Weil **One** Department **Many** employees hat.
 +
 +== @ManyToOne ==
 +{{http://i520.photobucket.com/albums/w327/schajtan/2014-11-06_10-55-15_zps527a98a0.png}}
 +
 +
 +
 +Then the modelling will look as following:
 +
 +<code>
 +@Entity
 +@Table(name="EMPLOYEE")
 +public class Employee {
 +  
 +    @ManyToOne
 +    @JoinColumn(name="department_id")
 +    private Department department;
 +}
 +@Entity
 +@Table(name="DEPARTMENT")
 +public class Department{
 +  
 +    @OneToMany(mappedBy="department")
 +    private Set<Employee> employees;
 +}
 +</code>
 +
 +
 +
 +==== Discriminators ====
 +Discriminators are used for for storing class hierarchies in a single table.
 +If having **RedCar.class** inheriting from **Car.class** - both stored in one table, then discriminator tells - hwne t orestore a RedCar and when t orestore a Car.
 +
 +//The element is required for polymorphic persistence using the table-per-class-hierarchy mapping strategy and declares a discriminator column of the table. The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row.
 +//
 +
 +Details: [[http://docs.jboss.org/hibernate/core/3.5/reference/en-US/html/mapping.html#mapping-declaration-discriminator]]
 +
 +
 +
 +
 +==== Spring ====
 +
 +=== ManyToOne ===
 +
 +The Domain Model looks as following
 +
 +<code>
 +Campus -is-> Location
 +CampusPart -is-> Location
 +
 +Campus -hasmany-> CampusPart
 +
 +</code>
 +
 +
 +
 +Introduce a Superclass with an ID.
 +<sxh java>
 +
 +@MappedSuperclass
 +@Table(name = "locations", schema = "public")
 +public abstract class ELocation {
 +
 +    @Id
 +    @Column(name = "ID", unique = true, nullable = false, insertable = false, updatable = false)
 +    protected UUID id = UuidCreator.getSequential();
 +}
 +</sxh>
 +
 +=== Bidirectional Many-To-Many ===
 +Here Both Entities Campus and CampuPart have a Link on each other.
 +This is called a BiDirectional modeling
 +
 +  * Owning-side vs. inverse-side https://www.baeldung.com/hibernate-one-to-many
 +
 +
 +== Owning - with Join Table ==
 +
 +Introduce an **ECampusPart**
 +Here the **JoinTable** is on CampusPart, so it is the **owning-side** of the connection. 
 +
 +<sxh java>
 +
 +@Entity(name = "ECampusPart")
 +@Table(name = "campusparts", schema = "public")
 +public class ECampusPart extends ELocation  {
 +
 +
 +  /*
 +    Modeling a bidirectional relationship here.
 +
 +    JoinTable means - that relationships will be extracted explicitely to an own table.
 +    Allows to explicitely name the columns: campuspart_id and campus_id here
 +
 +   */
 +  @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
 +  @JoinTable(
 +          name = "campus_campuspart",
 +          joinColumns = {@JoinColumn(name = "campuspart_id")},
 +          inverseJoinColumns = {@JoinColumn(name = "campus_id")}
 +  )
 +  private ECampus campus;
 +
 +</sxh>
 +
 +
 +Using "JoinTable" results in the creation of a special table
 +<sxh java>
 +
 +@Entity(name = "ECampusPart")
 +@Table(name = "campusparts", schema = "public")
 +public class ECampusPart extends ELocation  {
 +
 +  @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
 +  @JoinTable(
 +          name = "campus_campuspart",
 +          joinColumns = {@JoinColumn(name = "campuspart_id")},
 +          inverseJoinColumns = {@JoinColumn(name = "campus_id")}
 +  )
 +  private ECampus campus;
 +
 +</sxh>
 +
 +Results in
 +
 +**campuses**
 +^dtype^id^object_type^name^
 +|ECampusPart|497ffe9a-51bb-4ab4-bc3d-b8923d620703|CampusPart|ViennaSmart City Campus front part|
 +
 +
 +**campusparts**
 +^dtype^id^object_type^name^
 +|ECampus|dbadab87-7b6d-4e8b-b2c8-0663d68dfe7b|Campus|ViennaSmart City Campus|
 +
 +
 +**campus_campuspart**
 +^campus_id^campuspart_id^
 +|497ffe9a-51bb-4ab4-bc3d-b8923d620703|dbadab87-7b6d-4e8b-b2c8-0663d68dfe7b|
 +
 +
 +
 +== Owning-side - with embedded foreign key ==
 +
 +Omitting the "JoinTable" results in embedding the Foreign key **campus_id** - into the **CampusPart** table
 +
 +<sxh java>
 +
 +@Entity(name = "ECampusPart")
 +@Table(name = "campusparts", schema = "public")
 +public class ECampusPart extends ELocation  {
 +
 +  @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
 +  private ECampus campus;
 +
 +</sxh>
 +
 +
 +Results in 
 +
 +**campuses**
 +^dtype^id^object_type^name^campus_id^
 +|ECampusPart|497ffe9a-51bb-4ab4-bc3d-b8923d620703|CampusPart|ViennaSmart City Campus front part|dbadab87-7b6d-4e8b-b2c8-0663d68dfe7b|
 +
 +
 +**campusparts**
 +^dtype^id^object_type^name^
 +|ECampus|dbadab87-7b6d-4e8b-b2c8-0663d68dfe7b|Campus|ViennaSmart City Campus|
 +
 +
 +
 +
 +
 +== Inverse-side ==
 +
 +Introduce an **ECampus**.
 +Here the **mappedBy** is on Campus, so it is the **inverse-side** of the connection. 
 +
 +<sxh java>
 +
 +@Entity(name="ECampus")
 +@Table(name = "campuses", schema = "public")
 +public class ECampus extends ELocation  {
 +
 +  @OneToMany(mappedBy = "campus", cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
 +  private Set<ECampusPart> campusParts = new HashSet<>();
 +  
 +}
 +</sxh>
 +
 +
 +
 +
 +
 +===== Warning: bidirectional relationships ======
 +
 +ManyToMany relationships can lead to inconsitancies when
 +  * settings links back
 +  * there are multiple entities linking, back to parents 
 +  * entities have inconsistant list of members
 +{{https://s3.eu-central-1.amazonaws.com/alf-digital-wiki-pics/sharex/2020-05-05_12-01-00.png}}
 +
 +
 +
 +===== Transactional =====
 +
 +Seems like when wiring entities, which are affected on both sides - it should happen in 1 transaction.
 +
 +In Spring boot you need to annotate a method with ''@Transactional''
 +
 +At a high level, Spring creates proxies for all the classes annotated with @Transactional, either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method, mainly for starting and committing the transaction.
 +
 +https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
 +
 +
 +https://thorben-janssen.com/transactions-spring-data-jpa/
 +
 +<sxh java>
 +@Service
 +@Getter
 +public class EquipmentService {
 +
 +    @Autowired
 +    private EquipmentRepository equipmentRepository;
 +     
 +    @Transactional
 +    public void updateEquipmentFeeds(ResourcePK equipmentFeeds, ResourcePK equipmentTarget) {
 +        Assert.isTrue(equipmentFeeds.getPartitionId().equals(equipmentTarget.getPartitionId()), "Only allow feeds in same partition");
 +        Equipment e1 = equipmentRepository.findById(equipmentFeeds).get();
 +        Equipment e2 = equipmentRepository.findById(equipmentTarget).get();
 +        e1.getFeeds().add(e2);
 +        equipmentRepository.save(e1);
 +    } 
 +}
 +</sxh>
 +
 +
 +
 +==== ManyToMany with composite key to self ====
 +
 +<sxh java>
 +
 +    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
 +    @JoinTable(name="FeedsRel",
 +            joinColumns={
 +                    @JoinColumn(
 +                            name = "feedsrel_child_id",
 +                            referencedColumnName = "id"),
 +                    @JoinColumn(
 +                            name = "feedsrel_child_partitionId",
 +                            referencedColumnName = "partitionId")
 +            })
 +    private Set<Equipment> feeds = new HashSet<Equipment>();
 +
 +
 +    // Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn
 +    @ManyToMany( cascade = CascadeType.ALL, mappedBy="feeds", fetch = FetchType.EAGER)
 +    private Set<Equipment> fedBy = new HashSet<Equipment>();
 +</sxh>