Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1830 lines (1329 sloc) 33.4 KB

JPA/Hibernate camp

1. O mnie

  • Architect Solution - RiscoSoftware

  • VavaTech trener : Spring ekosystem, JPA , EIP Camel

  • Sages trener : JPA , EIP - Apache Camel

  • blog http://przewidywalna-java.blogspot.com

  • twitter przodownikR1

2. Źródła wiedzy

  • Hibernate in Action

  • Java Persistence with Hibernate

  • Java JEE 6

  • Pro JPA 2

  • Pro JPA 2: Mastering the Java™ Persistence API (Expert’s Voice in Java Technology)

  • Hibernate from Novice to Professional

  • Spring Data Modern Data Access for Enterprise Java

  • Spring Data

  • Spring Boot

  • Spring Essentials

  • Spring in Action

  • etc

3. Hibernate / JPA

3.1. Pola

Ten rodzaj mapowania oparty jest na typach i nazwach pól.

3.2. Metody

Na poziomie właściwości klasy. Adnotację umieszcza się przy @Getter.

3.3. Mieszany @Access

  • Przykład

@Entity
@Access(AccessType.FIELD)
public class Book{
@Id
@Setter
@Getter
private Long id;

private String name;

@Access(AccessType.PROPERTY)
public String getName()...
public void setName(String name)...
}

4. @Entity

  • oznacza klasy klasy, które mają brać udział w procesie utrwalania

  • taka klasa może być abstract

  • klasa nie może być final

  • klasa nie może zawierać pola i metody final

  • klasa musi posiadać bezargumentowy konstruktor

  • klasa musi posiadać klucz główny @Id

Caution
Dla komunikacji (distributed/web/session/serializable) powinna implementować Serializable. Klasa posiada wyróżnioną tożsamość
  • Przykład

@Entity
public class Simple {
    ...
}

5. Klucze @Id

5.1. Prosty

5.2. Biznesowe

  • @NaturalId

    • Rekomendowany zamiast klucza techniczego

    • Unikalny w ramach tabeli

    • JPA

      • @Table(uniqueConstraints = @UniqueConstraint(columnNames={column_1, …​, column_n})) , nullable = false

      • @EmbeddedId, @IdClass

public class Book {
   @Id
   @GeneratedValue
   private Long id;

   private String name;
   ....
   @NaturalId
   private String isbn;
   ...
}

5.3. Ustawianie wartości

5.3.1. ręcznie

5.3.2. automatyczne

  • każda klasa encyjna musi posiadać unikalny identyfikator.

    • Przykład

@Id
public Long id;
  • Database sequence - wykorzystuje sekwencje. Wsparcie dla baz DB2, PostgreSQL, Oracle, SAP DB.

    • Eliminacja 'roundtrip database' : hi/lo, pooled etc

  • Native generator - wybiera jedną ze strategii generowania identyfikatorów : identity, sequence, hilo w zależności od możliwości bazy

    • wsparcie dla kolumn identity w bazach DB2, MySQL, MS SQL Server, Sybase, HypersonicSQL.

  • Increment generator (Identity)

    • Identity uniemożliwia jdbc batch insert

  • Hilo generator - identyfikatory są unikalne w ramach całej bazy.

  • UUID - generuje Stringi (unikalny w sieci adres ip + znacznik czasu). Jest to kosztowne rozwiązanie.

    • Baza danych : UUID type → Binary(16) → char(32)

    • Oracle RAW(16)

  • assigned - pozwala aplikacji nadać identyfikator zanim obiekt zostanie zapisany.(persist, save)

5.3.3. @TableGenerator

  • używa blokad na wierszach

  • używa osobnej transakcji na połączenie

  • klucze główne trzymane są w specjalnej tabeli

    • Przykład

@TableGenerator(name="Book_Gen", table="ID_GEN",pkColumnName="GEN_NAME", valueColumnName="GEN_VAL", initialValue=10000, allocationSize=100)

@Id
@GeneratedValue(strategy = GenerationType.TABLE,generator="Book_Gen")
private Long id;
 @Entity public class Employee {
        ...
        @TableGenerator(
            name="empGen",
            table="ID_GEN",
            pkColumnName="GEN_KEY",
            valueColumnName="GEN_VALUE",
            pkColumnValue="EMP_ID",
            allocationSize=1)
        @Id
        @GeneratedValue(strategy=TABLE, generator="empGen")
        int id;
        ...
    }

5.3.4. @SequenceGenerator

  • Przykład

@SequenceGenerator(name="Book_Gen", sequenceName="Book_Seq",initialValue=10000,allocationSize=100)

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="Book_Gen")
private Long id;

5.4. Klucz złożony

5.4.1. @Id i @IdClass

  • Przykład

public class BookPK implements Serializable{

private String name;
private String isbn;
public int hashCode() {
return ...;
}
public boolean equals(Object obj) {
return ...;
}
}
@IdClass(BookPK.class)
@Entity
public class Book{
@Id
private String id;
@Id
private String isbn;
}

5.4.2. @EmbeddedId

  • Przykład

@Entity
class User {
   @EmbeddedId
   @AttributeOverride(name="firstName", column=@Column(name="fld_firstname")
   UserId id;

   Integer age;
}

@Embeddable
class UserId implements Serializable {
   String firstName;
   String lastName;
}

NOTE : Może być wykorzystywany przez @ElementCollection

5.5. ElementCollection

  • Przykład

@Entity
public class Employee {
  @Id
  @Column(name="EMP_ID")
  private long id;
  ...
  @ElementCollection
  @CollectionTable(
        name="PHONE",
        joinColumns=@JoinColumn(name="OWNER_ID")
  )
  private List<Phone> phones;
  ...
}
@Embeddable
public class Phone {
  private String type;
  private String areaCode;
  @Column(name="P_NUMBER")
  private String number;
  ...
}

5.6. AttributeOverride

  • Przykład

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @Embedded
  @AttributeOverrides({
    @AttributeOverride(name="startDate", column=@Column(name="START_DATE")),
    @AttributeOverride(name="endDate", column=@Column(name="END_DATE"))
  })
  private Period employmentPeriod;
  ...
}

@Entity
public class User {
  @Id
  private long id;
  ...
  @Embedded
  @AttributeOverrides({
    @AttributeOverride(name="startDate", column=@Column(name="SDATE")),
    @AttributeOverride(name="endDate", column=@Column(name="EDATE"))
  })
  private Period period;
  ...
}

6. @Table

  • domyślnie nazwa tabeli jest taka sama jak nazwa klasy.

  • jeśli domyślne ustawienie jest nie wystarczające z różnych powodów możemy użyć @Table

    • Przykład

@Table(name = "ITEMS",uniqueConstraints =@UniqueConstraint(name = "UNQ_NAME",columnNames = { "ITEM_NAME" })
)
public class Item extends AbstractEntity {

    private static final long serialVersionUID = 5474170031394030929L;
    @Column(name="ITEM_NAME")
    private String name;
    }
    create table ITEMS (
        id bigint not null,
        ITEM_NAME varchar(255),
        primary key (id)
    )
    alter table ITEMS  add constraint UNQ_NAME unique (ITEM_NAME)

6.1. Określenie schematu bazy w persistence.xml

<entity-mappings>
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <schema name="purchasing"/>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
    ....
</entity-mappings>

6.2. @Index

  • Przykład

@Table(name = "ITEMS",
indexes = {@Index(name = "IDX_USERNAME", columnList = "ITEM_NAME")}
public class Item extends AbstractEntity {

    @Column(name="ITEM_NAME")
    private String name;
    }
 create table ITEMS (
        id bigint not null,
        ITEM_NAME varchar(255),
        primary key (id)
    )
     create index IDX_USERNAME on ITEMS (ITEM_NAME)
Warning
Index Shotgun
  • Stosowanie indeksu bez żadnego planu

    • całkowity brak lub zbyt mało indeksów

    • zbyt dużo indeksów, które niczego nie wnoszą

    • wykonywanie zapytań, które nie bazują na indeksach

    • nadmiarowe indeksy złożone

    • indeksy pokrywające - wynik zapytania pochodzi tylko z indeksu , bez odczytywania wierszy z tabeli

Warning
indeksowanie długich łańuchów typów

NOTE : Zasada MENTOR

  • MEASURE (mierzenie) - wykrycie, które zapytania zajmują najwięcej czasu, monitoriwanie i wykrywanie wąskich gardeł

    • TKProf

    • SQL Trace

    • Dziennik zdarzeń

    • pgFouine

Note
Wszechobecny monitoring
  • Explain (wyjaśnianie) - plan wykonania QEP

    • Polecenie Explain

  • Nominate (Wskazywanie) - gdzie zapytanie dostaje dane bez użycia indeksu

    • MySql Enterprise Query Analyzer

    • Oracle Automatic SQL Tuning Advisor

  • Test (Testowanie) - po optymalizacji powtarzamy pierwsze kroki (Measure, Explain, ewentualnie Nominate )

  • Optimize (Optymalizacja)

    • pamięć podręczna vs I/O

    • Mysql : LOAD INDEX INTO CACHE

  • Rebuild (Przebudowa)

    • zrównoważenie. optymalny/niezrównoważony (jak defragmentacja systemu plików)

    • konserwacja indeksów

    • Mysql : Analyze Table , Optimize Table

    • Oracle : Alter Index …​. Rebuild

    • PostgreSQl : Vacuum , Analyze

7. @Column

  • analogiczne zachowanie do adnotacji @Table

  • insertable/updatable - określa czy dana kolumna będzie brała udział w operacjach insert/update

    • zapewnienie o read-only

    • zapewnia, że dostawca nie będzie brał udziału w aktualizacja lub dodaniu danych

    • @Column, @JoinColumn(name="BookStoreId", insertable=false, updatable=false)

  • unique - określa czy dana kolumna ma być traktowana jako klucz unikalny

  • nullable - czy kolumna może lub nie pozwalać na wartości null

  • length - długość kolumny dla String’a

  • precision - precyzja dla wartości BigDecimal

    • Przykład

    @Column(name = "retryattempt", columnDefinition = "numeric", nullable = true)
    private int retryAttempt = 0;

    @Column(name = "messageerror", columnDefinition = "nvarchar")
    private String messageError;

    @Column(name = "messagebody", length = Integer.MAX_VALUE, columnDefinition = "nvarchar")
    private String body;

    @Column(name = "detailstatus", columnDefinition = "nvarchar")
    @Enumerated(EnumType.STRING)
    private DetailStatus status;
  • Przykład 1

 @Column(nullable=false,scale=2,precision=2)
 private BigDecimal price;
 price decimal(2,2) not null
  • Przykład 2

 @Column
 private BigDecimal price;
 price decimal(19,2)
  • Przykład 3

@Column(name="ITEM_NAME",length=20,unique=true)
create table Item (
        id bigint not null,
        version bigint,
        ITEM_NAME varchar(20),
        price decimal(2,2) not null,
        primary key (id)
    )
     alter table Item  add constraint UK_bjye5lp3xnccmg4ovtumigp3v unique (ITEM_NAME)
  • Przykład 4

 @Column(columnDefinition ="varchar(15) not null unique check (not substring(lower(OWNER), 0, 5) = 'admin')")
 private String owner;
 create table Item (
        id bigint not null,
        version bigint,
        ITEM_NAME varchar(20),
        owner varchar(15) not null unique check (not substring(lower(OWNER), 0, 5) = 'admin'),
        price decimal(2,2) not null,
        primary key (id)

8. @Check

  • Przykład

@org.hibernate.annotations.Check(
constraints = "AUCTIONSTART < AUCTIONEND"
)
public class Offer extends AbstractEntity{
@NotNull
protected Date auctionStart;
@NotNull
protected Date auctionEnd;
}
  create table Offer (
        id bigint not null,
        version bigint,
        auctionEnd binary(255) not null,
        auctionStart binary(255) not null,
        offer_value decimal(19,2),
        ITEM_ID bigint not null,
        primary key (id),
        check (AUCTIONSTART < AUCTIONEND)
    )

9. @Transient

  • pole nie podlega procesowi utrwalania

10. @Basic

  • określa czy pole ma być opcjonalne (przydatne podczas generowania schematu przez Hibernate).

  • określa również sposób pobierania danych, czy pole ma być wypełniane od razu przy odczycie obiektu czy dopiero przy pierwszym odwołaniu.

11. @Embeddable i @Embedded**

  • umożliwia osadzanie nieencyjnych obiektów Java w objektach encyjnych

Embeddable

  • Przykład

@Embeddable
public class EmploymentPeriod {
  @Column(name="START_DATE")
  private java.sql.Date startDate;

  @Column(name="END_DATE")
  private java.sql.Date endDate;
  ....
}
@Entity
public class Employee {
  @Id
  private long id;
  ...
  @Embedded
  private EmploymentPeriod period;
  ...
}
@Embeddable
public class Address {

    private String line1;

    private String line2;

    @Embedded
    private ZipCode zipCode;

    ...

    @Embeddable
    public static class Zip {

        private String postalCode;

        private String plus4;

        ...
    }
}
@Entity
public class Person {

    @Id
    private Integer id;

    @Embedded
    private Name name;

    ...
}

@Multiple embeddable types

@Entity
public class Contact {

    @Id
    private Integer id;

    @Embedded
    private Name name;

    @Embedded
    private Address homeAddress;

    @Embedded
    private Address mailingAddress;

    @Embedded
    private Address workAddress;

    ...
}

@AttributeOverride

  • Przykład

@Entity
public class Contact {

    @Id
    private Integer id;

    @Embedded
    private Name name;

    @Embedded
    @AttributeOverrides(
        @AttributeOverride(
            name = "line1",
            column = @Column( name = "home_address_line1" ),
        ),
        @AttributeOverride(
            name = "line2",
            column = @Column( name = "home_address_line2" )
        ),
        @AttributeOverride(
            name = "zipCode.postalCode",
            column = @Column( name = "home_address_postal_cd" )
        ),
        @AttributeOverride(
            name = "zipCode.plus4",
            column = @Column( name = "home_address_postal_plus4" )
        )
    )
    private Address homeAddress;

    @Embedded
    @AttributeOverrides(
        @AttributeOverride(
            name = "line1",
            column = @Column( name = "mailing_address_line1" ),
        ),
        @AttributeOverride(
            name = "line2",
            column = @Column( name = "mailing_address_line2" )
        ),
        @AttributeOverride(
            name = "zipCode.postalCode",
            column = @Column( name = "mailing_address_postal_cd" )
        ),
        @AttributeOverride(
            name = "zipCode.plus4",
            column = @Column( name = "mailing_address_postal_plus4" )
        )
    )
    private Address mailingAddress;

    @Embedded
    @AttributeOverrides(
        @AttributeOverride(
            name = "line1",
            column = @Column( name = "work_address_line1" ),
        ),
        @AttributeOverride(
            name = "line2",
            column = @Column( name = "work_address_line2" )
        ),
        @AttributeOverride(
            name = "zipCode.postalCode",
            column = @Column( name = "work_address_postal_cd" )
        ),
        @AttributeOverride(
            name = "zipCode.plus4",
            column = @Column( name = "work_address_postal_plus4" )
        )
    )
    private Address workAddress;

    ...
}

12. @Enumerated

  • mapowanie enum

    • Przykład

@Entity
public class Person {
   @Enumerated
    public Gender gender;
    public static enum Gender {
        MALE,
        FEMALE
    }
}
  • @AttribureConverter

    • Przykład

public enum Gender {

    MALE('M'),
    FEMALE('F');

    private final char code;

    private Gender( char code ) {
        this.code = code;
    }

    public static Gender fromCode( char code ) {
        if ( code == 'M' || code == 'm' ) {
            return MALE;
        }
        if ( code == 'F' || code == 'f' ) {
            return FEMALE;
        }
        throw...
    }

    public char getCode() {
        return code;
    }
}

@Entity
public class Person {
    ...

    @Basic
    @Convert( converter = GenderConverter.class )
    public Gender gender;
}

@Converter
public class GenderConverter implements AttributeConverter<Character, Gender> {

    public Character convertToDatabaseColumn( Gender value ) {
        if ( value == null ) {
            return null;
        }

        return value.getCode();
    }

    public Gender convertToEntityAttribute( Character value ) {
        if ( value == null ) {
            return null;
        }

        return Gender.fromCode( value );
    }
}

13. @Lob

13.1. java.sql.Blob

@Entity
public class Step {
    ...
    @Lob
    @Basic
    public byte[] instructions;
    ...
}

13.2. java.sql.Clob

@Entity
public class Product {
    ...
    @Lob
    @Basic
    public Clob description;
    ...
}
  • Przykład

@Entity
public class Product {
    ...

    @Lob
    @Basic
    public Clob description;
    ...

    @Lob
    @Basic
    public char[] description;


    @Lob
    @Basic
    public Blob instructions;

     @Lob
    @Basic
    public byte[] instructions;
}
Note
Partycjonowanie : 'Vertical Partitioning' np z użyciem @OneToOne
Caution
Wydajność
Note
Użyj systemu plików
Note
Niezależna tabela

14. Date & time

14.1. DATE

  • java.sql.Date

14.2. TIME

  • java.sql.Time

14.3. TIMESTAMP

  • java.sql.Timestamp

15. Mapping Java 8 Date/Time Values

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-java8</artifactId>
    <version>${hibernate.version}</version>
</dependency>

15.1. DATE

  • java.time.LocalDate

INSERT INTO DateEvent( timestamp, id ) VALUES ( '2015-12-29', 1 )

15.2. TIME

  • java.time.LocalTime

  • java.time.OffsetTime

INSERT INTO DateEvent( timestamp, id ) VALUES ( '16:51:58', 1 )

15.3. TIMESTAMP

  • java.time.Instant,

  • java.time.LocalDateTime

  • java.time.OffsetDateTime

  • java.time.ZonedDateTime

INSERT INTO DateEvent  ( timestamp, id ) VALUES ( '2015-12-29 16:54:04.544', 1

16. AttributeConverters

  • Przykład

@Converter
public class PeriodStringConverter implements AttributeConverter<Period, String> {

    @Override
    public String convertToDatabaseColumn(Period attribute) {
        return attribute.toString();
    }

    @Override
    public Period convertToEntityAttribute(String dbData) {
        return Period.parse(dbData);
    }
}

@Entity
public class Event  {
    @Convert(converter = PeriodStringConverter.class)
    private Period span;

}

17. @Formula

  • Przykład

@Formula("obj_length * obj_height * obj_width")
private long objectVolume;


@Formula("UPPER(name)")
private String capitalName;

@Formula("(SELECT c.name FROM category c WHERE c.id=category_id)")
private String categoryName;

18. @SecondaryTable

Emp Tables (Database)
@Entity
@Table(name="EMPLOYEE")
@SecondaryTable(name="EMP_DATA",
                pkJoinColumns = @PrimaryKeyJoinColumn(name="EMP_ID", referencedColumnName="ID")
               )
public class Employee {
    ...
    @Column(name="YEAR_OF_SERV", table="EMP_DATA")
    private int yearsOfService;

    @OneToOne
    @JoinColumn(name="MGR_ID", table="EMP_DATA", referencedColumnName="ID")
    private Employee manager;
    ...
}

19. @AttributeOverride

  • Patrz wyżej w przykładzie z @Embedded.

20. @Version - blokowanie optymistyczne

  • Przykład

Employee employee = new Employee();
employee.setId(1);
employee.setName("przodownik");
session.saveOrUpdate(employee);
Hibernate: update employee set name=?, version=? where id=? and version=?

21. @OrderColumn

  • Przykład

@OrderColumn(name = "index_id")
    private List<Change> changes = new ArrayList<>();

22. @ForeignKey

  • Przykład

@Entity
public class Phone {
 @ManyToOne
    @JoinColumn(name = "person_id",
            foreignKey = @ForeignKey(name = "PERSON_ID_FK")
    )
    }
CREATE TABLE Phone (
    id BIGINT NOT NULL ,
    number VARCHAR(255) ,
    person_id BIGINT ,
    PRIMARY KEY ( id )
 )

ALTER TABLE Phone ADD CONSTRAINT PERSON_ID_FK FOREIGN KEY (person_id) REFERENCES Person

23. @Type (Hibernate only)

  • Przykład

@org.hibernate.annotations.Type( type = "nstring" )
private String name;

@org.hibernate.annotations.Type( type = "materialized_nclob" )
private String description;

@UniqueConstraint(columnNames = { "id" , "empCode"}))

24. @ElementCollection - dla typów prostych lub klas osadzonych

  • Przykład

@ElementCollection(fetch=FetchType.LAZY)
@CollectionTable(name = "email")
@IndexColumn(name="email_index")
private List<String> emails;

@ElementCollection(targetClass = CarBrands.class)
@Enumerated(EnumType.STRING)
private List<CarBrands> brands;
}

public enum CarBrands {
SUZUKI, STAR, FERRARI,JAGUAR;
}

25. @OrderBy

  • kolekcja może zostać uporządkowana według określonych kryteriów

  • w przypadku kolekcji uporządkowanej wykorzystać należy typ List

  • wykonywany przez wyrażenie SQL SELECT

@Entity
public class Book {
@ElementCollection
@CollectionTable(name = "Chapters")
@Column(name = "shortDest")
@org.hibernate.annotations.OrderBy(clause = "shortDesc desc")
protected Set<String> chapters = new LinkedHashSet<String>();

}
Note
@SortNatural i @SortComparator
  • Przykład

@OneToMany(mappedBy="user")
@OrderBy("lastName")
protected List<User> children;

26. @JoinTable

  • name to nazwa tabeli

  • joinColumns – kolumna tabeli złączenia, stanowiąca klucz dla encji

  • inverseJoinColumns – kolumna tabel złączenia, stanowiąca klucz dla encji po drugiej stronie relacji

27. Relacje

27.1. FetchType

Strategia

Domyślny tryb

OneToMany

LAZY

ManyToOne

EAGER

ManyToMany

LAZY

OneToOne

EAGER

@JoinColumn + @JoinTable

  • One-To-One 1:1

@Entity
public class Message {
@Id
Long id;

@Column
String content;

@OneToOne
Email email;

}
//ommit mutators and accessors
}
  • One-To-Many 1:N Za pomoca kluczu obcego

    • Przykład

@Entity
public class Item extends AbstractEntity {
    private String name;
    private BigDecimal price;

    @OneToMany(fetch = FetchType.LAZY) // Defaults to EAGER
    @JoinColumn(name = "ITEM_ID")
    private List<Offer> offers;


}
  • Przykład

@Entity
public class Offer extends AbstractEntity{
    @Column(name="offer_value")
    private BigDecimal value;
}
  • Generowany SQL :

  create table Item (
        id bigint not null,
        version bigint,
        name varchar(255),
        price decimal(19,2),
        primary key (id)
    )

     create table Offer (
        id bigint not null,
        version bigint,
        offer_value decimal(19,2),
        ITEM_ID bigint,
        primary key (id)
    )

     alter table Offer
        add constraint FKp6fm8wffictppkc0m3ufurbpy
        foreign key (ITEM_ID)
        references Item

Za pomoca kluczu głównego

  • Many-To-One N:1

    • Przykład

@Entity
public class Item  extends AbstractEntity{
    private String name;
    private BigDecimal price;
}
@Entity
public class Offer extends AbstractEntity{
    @ManyToOne(fetch = FetchType.LAZY) // Defaults to EAGER
    @JoinColumn(name = "ITEM_ID", nullable = false,
    foreignKey = @ForeignKey(name = "FK_ITEM_ID") )
    private Item item;

    @Column(name="offer_value")
    private BigDecimal value;
 }
 create table Item (
        id bigint not null,
        version bigint,
        name varchar(255),
        price decimal(19,2),
        primary key (id)
    )
      create table Offer (
        id bigint not null,
        version bigint,
        offer_value decimal(19,2),
        ITEM_ID bigint not null,
        primary key (id)
    )
    alter table Offer
        add constraint FK_ITEM_ID
        foreign key (ITEM_ID)
        references Item
  • Many-To-Many N:M

public class ProjectType {
  @Id
  @GeneratedValue
  private long id;
  @ManyToOne
  private Employee employee;
  @Column(name="PROJ_TYPE")
  private String type;
  @ManyToMany
  private List<Project> projects;
}
  • @ManyToAny

Pozwala na polimorficzne asocjacje klas z różnumi tabelami

public interface Property {
    public String getName();
    public String asString();
}
@Entity
@Table(name = "long_property")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class LongProperty extends AbstractEntity implements Property {

    private static final long serialVersionUID = -1275713912865305945L;

    private String name;

    private Long value;

    @Override
    public String asString() {
        return Long.toString(value);
    }

    @Override
    public String getName() {
        return name;
    }

}
@Entity
@Table(name="string_property")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class
StringProperty extends AbstractEntity implements Property {

    private static final long serialVersionUID = -4928360703691383693L;
    private String name;
    private String value;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String asString() {
        return value;
    }


    public void setName(String name) {
        this.name = name;
    }
}
@Entity
@Table(name = "Property_Example")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class PropertyMap extends AbstractEntity {

    private static final long serialVersionUID = 6842457337123716680L;

    private String name;
    @ManyToAny(metaColumn = @Column(name = "property_type"))
    @AnyMetaDef(idType = "integer", metaType = "string", metaValues = { @MetaValue(value = "S", targetEntity = StringProperty.class),
            @MetaValue(value = "I", targetEntity = IntegerProperty.class) })
    @Cascade({ org.hibernate.annotations.CascadeType.ALL })
    @JoinTable(name = "obj_properties", joinColumns = @JoinColumn(name = "obj_id"), inverseJoinColumns = @JoinColumn(name = "property_id"))
    private List<Property> properties = newArrayList();

    public PropertyMap(String name) {
        this.name = name;
    }

}
create table Property_Example (
        id bigint not null,
        version bigint,
        name varchar(255),
        primary key (id)
    );
    create table obj_properties (
        obj_id bigint not null,
        property_type varchar(255),
        property_id integer not null
    );
    create table string_property (
        id bigint not null,
        version bigint,
        name varchar(255),
        value varchar(255),
        primary key (id)
    );
    create table long_property (
        id bigint not null,
        version bigint,
        name varchar(255),
        value bigint,
        primary key (id)
    );
  • Map - zamiennik strategii ManyToMany i trójskładnikowych związków asocjacyjnych

    • Klucz i wartość klasy Map może być typem prostem , encją lub typem wbudowanym

NOTE : Gdy wartość jest encją dodajemy @OneToMany lub @ManyToMany

Note
Gdy wartość jest typem prostym lub klasą zagnieżdzoną , dodajemy @ElementCollection
Warning
Map działa tylko po jednej stronie relacji dwukierunkowej
  • @MapKeyColumn - jeśli kluczem będzie typem bazowym

  • @MapKeyEnumerated - jeśli kluczem będzie enum

@Entity
public class Owner  extends AbstractEntity{

    @OneToMany
    @MapKeyEnumerated(EnumType.STRING)
    private Map<BookType, Book> bookMap;
}
  • @MapKeyTemporal -jeśli kluczem będzie typu Data/Calenadar

@Entity
public class Owner extends AbstractEntity{

    @OneToMany(mappedBy="owner")
    @MapKeyTemporal(TemporalType.TIMESTAMP)
    private Map<Date, Book> bookMap;
}
  • @MapKeyJoinColumn - jeśli kluczem mapy będzie encją

    • napisuje JOIN_COLUMN

@Entity
public class Owner {
    @Id
    private long id;


    @MapKeyJoinColumn(name="book publisher_id")
    private Map<Publisher, Book> bookMap;
}
  • @MapKey

chcemy zdecydować co będzie kluczem w naszej mapie.

  • Dla relacji one-to-many i many-to-many gdzie klucz jest jednym z atrybutów

27.2. Przykład 1

@Entity
public class Country extends AbstractEntity{

    @Column(name="name")
    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="country_id")
    @MapKey(name="id")
    private Map<Integer,State> states;

27.3. Przykład 2

public class Department extends AbstractEntity {
    private static final long serialVersionUID = -7670935289254672108L;
    private String name;
    private Long ids;
    @OneToMany(cascade = CascadeType.ALL)
    @MapKey
    Map<UUID, Phone> phones;
    }
  • @MapKeyColumn

public class Department extends AbstractEntity {
    private String name;

    @ElementCollection
    @CollectionTable(name = "subDept")
    @MapKeyColumn(name = "subDeptName")
    @Column(name = "subDeptShortName")
    protected Map<String, String> subDepts = new HashMap<>();
    }

28. @SortComparator

  • Sortowanie w pamięci Map i Set

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="bookId")
    @SortComparator(BookNameComparator.class)
    private SortedSet<Book> books;

    ...

    public class BookNameComparator implements Comparator<Book> {
    @Override
    public int compare(Book b1, Book b2) {
      return b1.getBookName().compareTo(b2.getBookName());
    }
}

29. @SortNatural

  • Sortowanie w pamięci Map i Set

   @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="book_id")
    @SortNatural
    private SortedSet books;
    ...

    public class Book implements Comparable<Book> {
    @Column(name = "writer")
    private String writer;
    @Override
    public int compareTo(Book o) {
        return writer.compareTo(o.getWriter());
    }
    }
Warning
W przypadku dużych kolekcji użyć : @OrderBy lub bezpośrednio order by w kwerendzie SQL.

30. @Dynamic

30.1. @DynamicInsert (false/true)

Manipulowanie operacjami Insert na poziomie encji. Wstawiamy tylko wybrane kolumny.

Note
Tuning. Potencjalne przyspieszenie dla dużych tabel w szególności.

30.2. @DynamicUpdate (false/true)

Manipulowanie operacjami Update na poziomie encji. Uaktualniamy tylko te kolumny, które się zmieniły

Note
Tuning. Potencjalne przyspieszenie dla dużych tabel w szczególności

31. @Immutable

@Entity
@Immutable
@Cache (usage=CacheConcurrencyStrategy.READ_ONLY)
@Table(name = "products")
public class Product extends AbstractEntity {

}
Note
@Immutable oznacza , że żadna modyfikacja na 'immutable entity' nie może się udać bez generacji wyjątku. NOTE: Może być stosowana dla kolekcji NOTE: Wyrzucany wyjątek to: HibernateException

32. @Synchronize

@org.hibernate.annotations.Synchronize({"Book", BookStore"})

33. @SubSelect - view

@Entity
@org.hibernate.annotations.Immutable
@org.hibernate.annotations.Subselect(
value = "select i.id as userId, i.firstName as name, " +
"count(a.id) as addressCount " +
"from ITEM i left outer join Address a on i.ID = a.id " +
"group by i.id"
)
@org.hibernate.annotations.Synchronize({"Item", "Bid"})
public class UserAddressStats {
@Id
protected Long userId;
protected String name;
protected long addressCount;
public ItemBidSummary() {
}

}
Note
@Synchronize, upewnij się, że encje Item i Bid są 'flushed' przed wykonaniem operacji na ItemBidSummary

34. Callbacks

34.1. @PrePersist

Wykonanie operacji przed operacją zapisu

34.2. @PreRemove

Wykonanie operacji przez operacją usunięcia

34.3. @PostPersist

Wykonanie operacji po operacji zapisu

34.4. @PostRemove

Wykonanie operacji po operacji usunięcia

34.5. @PreUpdate

Wykonanie kodu przed operacją aktualizacji

34.6. @PostUpdate

Wykonanie kodu po operacji aktualizacji

34.7. @PostLoad

Wykonanie akcji po załadowaniu encji z kontekstu trwałości

35. EventListener

  • Przykład

@Entity
@EntityListeners( LastUpdateListener.class )
public static class Person {

    @Id
    private Long id;

    private String name;

    private Date dateOfBirth;

    @Transient
    private long age;

    private Date lastUpdate;

    @PostLoad
    public void calculateAge() {
        age = ChronoUnit.YEARS.between( LocalDateTime.ofInstant(
                Instant.ofEpochMilli( dateOfBirth.getTime()), ZoneOffset.UTC),
            LocalDateTime.now()
        );
    }
}

public static class LastUpdateListener {

    @PreUpdate
    @PrePersist
    public void setLastUpdate( Person p ) {
        p.setLastUpdate( new Date() );
    }
}