Ejb3 In Action Cap8 Object-relational mapping

introduzione inutile

8.1

niente interessante

8.2 Mapping entities

@Entity
@Table(name="USERS") //la tabella che viene mappata
@SecondaryTable(name="USER_PICTURES", pkJoinColumns=@PrimaryKeyJoinColumn(name="USER_ID"))
//c'è un join con la tabella USER_PICTURES tramite la chiave primaria USER_ID
public class User implements Serializable {
//ogni campo è mappato sulla tabella con la notazione @Column
    @Id
    @Column(name="USER_ID", nullable=false)
    protected Long userId;
    @Column(name="USER_NAME", nullable=false)
    protected String username;
    @Column(name="FIRST_NAME", nullable=false, length=1)
    protected String firstName;
    @Column(name="LAST_NAME", nullable=false)
    protected String lastName;
 
    @Enumerated(EnumType.ORDINAL) //questo campo è ristretto a dei valori di Enumeration
    @Column(name="USER_TYPE", nullable=false)
    protected UserType userType;
 
   // caricamento pigro, lo carica dal db solo quando viene richiesto e non in automatico
   @Column(name="PICTURE", table="USER_PICTURES")
   @Lob
   @Basic(fetch=FetchType.LAZY)
    protected byte[] picture;              
    @Column(name="CREATION_DATE", nullable=false)
                                                        F Temporal field
    @Temporal(TemporalType.DATE)
    protected Date creationDate;
 
    @Embedded
    protected Address address;
    public User() {}
}
 
@Embeddable
public class Address implements Serializable {
    @Column(name="STREET", nullable=false)
    protected String street;

8.2.1 Specifying the table

L'annotazione @Table è opzionale se non viene specificata o il campo name è vuoto viene collegata una tabella che ha lo stesso nome dell'entità.
Si possono usare catalog and schema

@Table(name="USERS", schema="ACTIONBAZAAR")
public class User

Si può avere la generazione automatica delle tabelle ma è sconsigliata.
Una entità può avere una tabella secondaria è usata l'annotazione @SecondaryTable come si vede nell'esempio sovrastante dove le immagini vengono gestiti come BLOB in un'altra tabella.

8.2.2 Mapping the columns

Per mappare una colonna

@Column(name="USER_ID")
protected Long userId;

Se ci sono più tabelle si può usare la sintassi
@Column(name="PICTURE", table="USER_PICTURES")
...
protected byte[] picture;

Come si può vedere dall'interfacci vi sono altre opzioni
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Column {
    String name() default "";
    boolean unique() default false; // Specifies unique constraint
    boolean nullable() default true; //Specifies if column allows nulls                                                   
    boolean insertable() default true; //impedisce l'inserimento quando viene fatta la insert nel db
    boolean updatable() default true; //impedisce l'aggiornamento quando viene generata la update
    String columnDefinition() default "";
    String table() default "";
    int length() default 255; //Length of column
    int precision() default 0; //Decimal precision of column
    int scale() default 0; //Decimal scale of column 
}

La insertable e la updatable possono essere utili quando si vuole usare la tabella in readonly eccone un esempio di utillizzo
@Column(name="USER_ID", insertable=false, updatable=false)
protected Long userId;

Inoltre nel caso della chiave primaria che è un contatore che si autoincrementa alla creazione si vuole fare in modo che sia il trigger del db a generarlo, inoltre quando si fa l'update non si aggiorna mai la chiave primaria.
Se l'annotazione non è presente o il nome non è specificato il campo della tabella deve avere lo stesso nome di quello dell'entità.

8.2.3 Using @Enumerated

In java 5 sono stati introdotti gli enumerated

public enum UserType {SELLER, BIDDER, CSR, ADMIN};

Non è altro che un vettore accedibile con gli indici.
Se usiamo questa annotazione
@Enumerated(EnumType.ORDINAL)

Sul db viene memorizzato l'ordinale quindi 0 o 1 ecc.
@Enumerated(EnumType.STRING)

Allora sul db viene memorizzata la stringa SELLER o CSR ecc.
Il valore di default è l'ordinale.

8.2.4 Mapping CLOBs and BLOBs

va be lasciamo stare per ora

8.2.5 Mapping temporal types

Vi sono diversi tipi di date

@Temporal(TemporalType.DATE)
protected Date creationDate;

DATE (storing day, month, and year), TIME (storing just time and not day, month, or year) and TIMESTAMP (storing time, day, month,and year). Il valore di default è timestamp

8.2.6 Mapping an entity to multiple tables

Quando si carica l'entità viene fatto il join delle tabelle

@Entity
@Table(name="USERS")
@SecondaryTable(name="USER_PICTURES",
    pkJoinColumns=@PrimaryKeyJoinColumn(name="USER_ID"))
public class User implements Serializable {
..}

Se ci sono più tabelle secondarie l'annotazione @SecondaryTable viene usata più volte.

8.2.7 Generating primary keys

Per far generare le chiavi primarie automaticamente si usa la notazione @GeneratedValue

Identity columns as generators

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="USER_ID")
protected Long userId;

Con IDENTITY il valore non sarà disponibile fino alla memorizzazione sul db, perchè la chiave viene creata in quel momento. Con le altre SEQUENCE and TABLE serve creare un generatore SequenceGenerator or TableGenerator.

Database sequences as generators

tralasciato

Sequence tables as generators

tralasciato

Default primary key generation strategy

L'ultima strategia è lasciare scegliere al provider la migliore strategia per usando AUTO
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="USER_ID")
protected Long userId;

8.2.8 Mapping embeddable classes

tralasciamo

8.3 Mapping entity relationships

8.3.1 Mapping one-to-one relationships

Si possono implementare le relazioni in due maniere @JoinColumn oppure @Primary-KeyJoinColumn in base a dove la chiave esterna risiede.

Using @JoinColumn

Ecco un esempio la tabella User ha una chiave esterna (USER_BILLING_ID) collegata alla primaria di BILLING_INFO (chiamata USER_BILLING_ID)

@Entity
@Table(name="USERS")
public class User {
    @Id
    @Column(name="USER_ID")
    protected String userId;
    ...
    @OneToOne
    @JoinColumn(name="USER_BILLING_ID",referencedColumnName="BILLING_ID", updatable=false)
    protected BillingInfo billingInfo;
}
@Entity
@Table(name="BILLING_INFO")
public class BillingInfo {
    @Id
    @Column(name="BILLING_ID")
    protected Long billingId;
    ...
}

I parametri dell'annotazione @JoinColumn sono updatable, insertable, table, and unique e hanno lo stesso significato che assumono nell'annotazione @Column vista prima. Il parametro settato nell'esempio sovrastante updatable=false indica che la chiave esterna lato User non viene aggiornata neanche se dal lato BILLING_INFO le informazioni dovessero cambiare.
Se si hanno più di una colonna nella chiave esterna si usa la notazione @JoinColumns ma è una soluzione preferibilmente da evitare.
Se si ha una relazione bidirezionale l'entità BillingInfo deve essere modificata come segue
@Entity
public class BillingInfo {
    @OneToOne(mappedBy="billingInfo")
    protected User user;
..
}

Using @PrimaryKeyJoinColumn

In questo caso le due tabelle vengono collegate entrambe attraverso le chiavi primarie

@Entity
@Table(name="USERS")
public class User {
    @Id
    @Column(name="USER_ID")
    protected Long userId;
    ...
    @OneToOne
    @PrimaryKeyJoinColumn(name="USER_ID", referencedColumnName="BILLING_USER_ID")
    protected BillingInfo billingInfo;
}
@Entity
@Table(name="BILLING_INFO")
public class BillingInfo {
    @Id
    @Column(name="BILLING_USER_ID")
    protected Long userId;
    ...
}

Se la chiave primaria è una composizione di più chiavi (cosa rara e non auspicabile) si usa la notazione @PrimaryKeyJoinColumns

8.3.2 One-to-many and many-to-one

@Entity
@Table(name="ITEMS")
public class Item {
    @Id
    @Column(name="ITEM_ID")
    protected Long itemId;
    ...
    @OneToMany(mappedBy="item")
    protected Set<Bid> bids;
    ...
}
@Entity
@Table(name="BIDS")
public class Bid {
    @Id
    @Column(name="BID_ID")
    protected Long bidId;
    ...
    @ManyToOne
    @JoinColumn(name="BID_ITEM_ID", referencedColumnName="ITEM_ID")
    protected Item item;
    ...
}

Sarà generato un errore se viene specificato una @JoinColumn da entrambi i lati di una relazione one-to-many bidirezionale.

tralasciato il resto

8.3.3 Many-to-many

Come sappiamo la relazione molti a molti viene mappata spezzandola in uno a molti mettendo una tabella di mezzo.

relazione-molti-a-molti

Il codice implementa la relazione dell'immagine.

@Entity
@Table(name="CATEGORIES")
public class Category implements Serializable {
    @Id
    @Column(name="CATEGORY_ID")
    protected Long categoryId;
    @ManyToMany
    @JoinTable(name="CATEGORIES_ITEMS",
        joinColumns= @JoinColumn(name="CI_CATEGORY_ID", referencedColumnName="CATEGORY_ID"),
        inverseJoinColumns= @JoinColumn(name="CI_ITEM_ID", referencedColumnName="ITEM_ID"))
    protected Set<Item> items;
    ...
}
@Entity
@Table(name="ITEMS")
public class Item implements Serializable {
    @Id
    @Column(name="ITEM_ID")
    protected Long itemId;
    ...
    @ManyToMany(mappedBy="items")
  protected Set<Category> categories;
  ...
}

continuare da pag 314

Salvo diversa indicazione, il contenuto di questa pagina è sotto licenza Creative Commons Attribution-ShareAlike 3.0 License