Thursday, November 16, 2017

Postgres JSONB storage capabilities

JSONB Features

  • JSONB data input is little slower, but processing is then significantly faster because the data does not need to be re-parsed
  • JSONB could be restricted by data constraint and validation functions
  • JSONB is a efficient representation with indexing capability
  • JSONB is efficient in the storage and retrieval of JSON documents, but the modification of individual fields requires the extraction of the entire JSON document

Rapid Prototyping

  • The data stored is schema-less, as the business requirements rapidly change there is no effort needed to continuously write migrations
  • No effort is required to think through a data-model, ensuring proper normalization
  • No need to write SQL
  • The data is sub-optimal importance, it is acceptable of rarely data loss or corruption, thus the strong guarantees provided by a standard RDBMS are not necessary

ACID

Atomicity

each transaction should be "all or nothing"

Consistency

any transaction will bring the database from one valid state to another, any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof.

Isolation

the concurrent execution of transactions results in a system state that would be obtained if transactions were executed sequentially, i.e., one after the other.

Durability

Once a transaction has been committed, it will remain so, even in the event of power loss.

CAP Theorem

In the presence of a network partition, one has to choose between consistency and availability.

Consistency

Every read receives the most recent write or an error

Availability

Every request receives a (non-error) response - without guarantee that it contains the most recent write

Partition Tolerance

The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes

It is really just A vs C

Availability is achieved by replicating the data across different machines
Consistency is achieved by updating several nodes before allowing further reads
Total partitioning, meaning failure of part of the system is rare. However, we could look at a delay, a latency, of the update between nodes, as a temporary partitioning. It will then cause a temporary decision between A and C:
1. On systems that allow reads before updating all the nodes, we will get high availability
2. On systems that lock all the nodes before allowing reads, we will get consistency

Level of Transaction Isolation

Read Committed
Repeatable Read
Serialization

Document Database

are designed to store semi-structured data that there is no clear separation between the data's schema and the data itself

Column-oriented DBMS

is a database management system (DBMS) that stores data tables by column rather than by row. A column-oriented database serializes all of the values of a column together, then the values of the next column, and so on.

Third Normal Form (3NF)

Each attribute contains only atomic values.
No data is redundantly represented based on any non-unique subsets. For every unique set of the entries (a candidate key), no other attribute depends on any subset of the candidate key
No data is dependent on anything other than the key


Tuesday, November 14, 2017

VirtualBox: key concepts and features

Key Concepts

·        Host operating system
·        Guest Operating System
·        Guest Additions
·        Virtual Machine (VM)
the special environment that VirtualBox creates for a guest OS while it is running.

Virtual Networking

VirtualBox provides up to 8 virtual PCI Ethernet cards. for each virtual machine.

Networking Modes

·        Network Address Translation (NAT)
It is the default value, it is used when no services are hosted. Services will be reachable with 127.0.0.1:port
·        Bridged Networking
It provides direct external access to the VMs. VMs will user the host physical network adapter directly.
·        Internal Networking
It is used to create a network containing the host and a set of virtual machines, without the need for the host's physical network interface. Every internal network is identified simply by its name, once there is more than one active virtual network card with the same internal network ID, the VirtuaBox support driver will automatically "wire" the cards and act as a network switch.
·        Host-only Networking
A virtual network interface is created on the host, it provides the connectivity among virtual machines and the host. A service hosted in a VM could use a Host-only Networking to talk to a database hosted in another VM, then use a bridged networking to expose services.

Remote Display (VRDP) Support

VirtualBox Remote Display Protocol (VRDP) is a backwards-compatible extension to Microsoft's Remote Desktop Protocol (RDP).

VBoxManage

Command-line interface to VirtualBox.

Importing and exporting Virtual Machines

VMs could be imported and exported using the Open Virtualization Format (OVF).

Wednesday, November 1, 2017

Oracle Locks: the V$LOCK v$session views and dba_ojbects

The original article is here. Save it here in case it is not available for some reason. It is one of the best article about this topic out there.



What's blocking my lock?


Natalka Roshak's picture

articles: 
If you've ever gotten a phone call from an annoyed user whose transaction just won't go through, or from a developer who can't understand why her application sessions are blocking each other, you know how useful it can be to identify not just whose lock is doing the blocking, but what object is locked. Even better, you can identify the exact row that a session is waiting to lock.

Create a blocking lock

To begin, create a situation where one user is actively blocking another. Open two sessions. Issue the following commands in Session 1 to build the test table:
SQL> create table tstlock (foo varchar2(1), bar varchar2(1));

Table created.

SQL> insert into tstlock values (1,'a'); 

1 row created.

SQL> insert into tstlock values (2, 'b');

1 row created.

SQL> select * from tstlock ;

FOO BAR
--- ---
1   a
2   b

2 rows selected.

SQL> commit ;

Commit complete.
Now grab a lock on the whole table, still in Session 1:
SQL> select * from tstlock for update ;
And in Session 2, try to update a row:
SQL> update tstlock set bar=
  2  'a' where bar='a' ;
This statement will hang, blocked by the lock that Session 1 is holding on the entire table.

Identify the blocking session

Oracle provides a view, DBA_BLOCKERS, which lists the SIDs of all blocking sessions. But this view is often, in my experience, a good bit slower than simply querying V$LOCK, and it doesn't offer any information beyond the SIDs of any sessions that are blocking other sessions. The V$LOCK view is faster to query, makes it easy to identify the blocking session, and has a lot more information.
SQL> select * from v$lock ;

ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
AF9E2C4C AF9E2C60        479 TX     131078      16739          0          6        685          0
ADDF7EC8 ADDF7EE0        422 TM      88519          0          3          0        697          0
ADDF7F74 ADDF7F8C        479 TM      88519          0          3          0        685          0
ADEBEA20 ADEBEB3C        422 TX     131078      16739          6          0        697          1
....     ....            ... ...      ....       ....       ....       ....        ....      ....
Note the BLOCK column. If a session holds a lock that's blocking another session, BLOCK=1. Further, you can tell which session is being blocked by comparing the values in ID1 and ID2. The blocked session will have the same values in ID1 and ID2 as the blocking session, and, since it is requesting a lock it's unable to get, it will have REQUEST > 0.
In the query above, we can see that SID 422 is blocking SID 479. SID 422 corresponds to Session 1 in our example, and SID 479 is our blocked Session 2.
To avoid having to stare at the table and cross-compare ID1's and ID2's, put this in a query:
SQL> select l1.sid, ' IS BLOCKING ', l2.sid
  2  from v$lock l1, v$lock l2
  3  where l1.block =1 and l2.request > 0
  4  and l1.id1=l2.id1
  5  and l1.id2=l2.id2
SQL> /

       SID 'ISBLOCKING'         SID
---------- ------------- ----------
       422  IS BLOCKING         479

1 row selected.
Even better, if we throw a little v$session into the mix, the results are highly readable:
SQL> select s1.username || '@' || s1.machine
  2  || ' ( SID=' || s1.sid || ' )  is blocking '
  3  || s2.username || '@' || s2.machine || ' ( SID=' || s2.sid || ' ) ' AS blocking_status
  4  from v$lock l1, v$session s1, v$lock l2, v$session s2
  5  where s1.sid=l1.sid and s2.sid=l2.sid
  6  and l1.BLOCK=1 and l2.request > 0
  7  and l1.id1 = l2.id1
  8  and l2.id2 = l2.id2 ;


BLOCKING_STATUS
----------------------------------------------------------------------------------------------------
BULKLOAD@yttrium ( SID=422 )  is blocking BULKLOAD@yttrium ( SID=479 )

1 row selected.
There's still more information in the v$lock table, but in order to read that information, we need to understand a bit more about lock types and the cryptically-named ID1 and ID2 columns.

Lock type and the ID1 / ID2 columns

In this case, we already know that the blocking lock is an exclusive DML lock, since we're the ones who issued the locking statement. But most of the time, you won't be so lucky. Fortunately, you can read this information from the v$lock table with little effort.
The first place to look is the TYPE column. There are dozens of lock types, but the vast majority are system types. System locks are normally only held for a very brief amount of time, and it's not generally helpful to try to tune your library cache, undo logs, etc. by looking in v$lock! (See the V$LOCK chapter in the Oracle Database Reference for a list of system lock types.)
There are only three types of user locks, TX, TM and UL. UL is a user-defined lock -- a lock defined with the DBMS_LOCK package. The TX lock is a row transaction lock; it's acquired once for every transaction that changes data, no matter how many objects you change in that transaction. The ID1 and ID2 columns point to the rollback segment and transaction table entries for that transaction.
The TM lock is a DML lock. It's acquired once for each object that's being changed. The ID1 column identifies the object being modified.

Lock Modes

You can see more information on TM and TX locks just by looking at the lock modes. The LMODE and REQUEST columns both use the same numbering for lock modes, in order of increasing exclusivity: from 0 for no lock, to 6 for exclusive lock. A session must obtain an exclusive TX lock in order to change data; LMODE will be 6. If it can't obtain an exclusive lock because some of the rows it wants to change are locked by another session, then it will request a TX in exclusive mode; LMODE will be 0 since it does not have the lock, and REQUEST will be 6. You can see this interaction in the rows we selected earlier from v$lock:
ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
AF9E2C4C AF9E2C60        479 TX     131078      16739          0          6        685          0
ADEBEA20 ADEBEB3C        422 TX     131078      16739          6          0        697          1
Note that ID1 and ID2 in Session 2, which is requesting the TX lock (LMODE=0, REQUEST=6), point back to the rollback and transaction entries for Session 1. That's what lets us determine the blocking session for Session 2.
You may also see TX locks in mode 4, Shared mode. If a block containing rows to be changed doesn't have any interested transaction list (ITL) entries left, then the session acquires a TX lock in mode 4 while waiting for an ITL entry. If you see contention for TX-4 locks on an object, you probably need to increase INITRANS for the object.
TM locks are generally requested and acquired in modes 3, aka Shared-Row Exclusive, and 6. DDL requires a TM Exclusive lock. (Note that CREATE TABLE doesn't require a TM lock -- it doesn't need to lock any objects, because the object in question doesn't exist yet!) DML requires a Shared-Row Exclusive lock. So, in the rows we selected earlier from v$lock, you can see from the TM locking levels that these are DML locks:
ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
ADDF7EC8 ADDF7EE0        422 TM      88519          0          3          0        697          0
ADDF7F74 ADDF7F8C        479 TM      88519          0          3          0        685          0

Identifying the locked object

Now that we know that each TM row points to a locked object, we can use ID1 to identify the object.
SQL> select object_name from dba_objects where object_id=88519 ;

OBJECT_NAME
--------------
TSTLOCK
Sometimes just knowing the object is enough information; but we can dig even deeper. We can identify not just the object, but the block and even the row in the block that Session 2 is waiting on.

Identifying the locked row

We can get this information from v$session by looking at the v$session entry for the blocked session:
SQL> select row_wait_obj#, row_wait_file#, row_wait_block#, row_wait_row#
  2* from v$session where sid=479 ;

ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW#
------------- -------------- --------------- -------------
        88519             16          171309             0
This gives us the object ID, the relative file number, the block in the datafile, and the row in the block that the session is waiting on. If that list of data sounds familiar, it's because those are the four components of an extended ROWID. We can build the row's actual extended ROWID from these components using the DBMS_ROWID package. The ROWID_CREATE function takes these arguments and returns the ROWID:
SQL> select do.object_name,
  2  row_wait_obj#, row_wait_file#, row_wait_block#, row_wait_row#,
  3  dbms_rowid.rowid_create ( 1, ROW_WAIT_OBJ#, ROW_WAIT_FILE#, ROW_WAIT_BLOCK#, ROW_WAIT_ROW# )
  4  from v$session s, dba_objects do
  5  where sid=543
  6  and s.ROW_WAIT_OBJ# = do.OBJECT_ID ;

OBJECT_NAME     ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW# DBMS_ROWID.ROWID_C
--------------- ------------- -------------- --------------- ------------- ------------------
TSTLOCK                 88519             16          171309             0 AAAVnHAAQAAAp0tAAA
And, of course, this lets us inspect the row directly.
SQL> select * from tstlock where rowid='AAAVnHAAQAAAp0tAAA' ;

FOO BAR
--- ---
1   a

Conclusion

We've seen how to identify a blocking session, and how to inspect the very row that the waiting session is waiting for. And, I hope, learned a bit about v$lock in the process.

Thursday, October 26, 2017

Hibernate Association Mapping

(Hibernate Document Notes)

Mapping Types

Value Types

  • Basic Types
  • Embeddable types
  • Collection Types

Entity Types


@Table(name = "phone")
@Entity(name = "Phone")
public static class Phone {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Basic // this is a default
    @Column(name = "phone_number")
    private String number;
 
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "phone_type")
    private PhoneType type;
 
    @Column(name = "`timestamp`")
    @Temporal(TemporalType.DATE)
    private Date timestamp;
 
}

@Target and @Parent mapping


The @Target annotation is used to specify the implementation class of a given association that is mapped via an interface. The @ManyToOne@OneToOne@OneToMany, and @ManyToMany feature a targetEntity attribute to specify the actual class of the entity association when an interface is used for the mapping.
The @ElementCollection association has a targetClass attribute for the same purpose.

public interface Coordinates {
    double x();
    double y();
}
 
@Embeddable
public static class GPS implements Coordinates {
 
...
}
 
@Entity(name = "City")
public static class City {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Embedded
    @Target( GPS.class )
    private Coordinates coordinates;
 
... 
}

The Hibernate-specific @Parent annotation allows you to reference the owner entity from within an embeddable.

@Embeddable
public static class GPS {
    @Parent
    private City city;
 
    private GPS() {
    }
 
...
}
 
@Entity(name = "City")
public static class City {
 
    @Id
    @GeneratedValue
    private Long id;
 
...
    @Embedded
    @Target( GPS.class )
    private Coordinates coordinates;
 
...
}

  Associations

@ManyToOne

It is the most common association, having a direct equivalent in the relational database association of foreign key.

@Entity(name = "Person")
public static class Person {
 
    @Id
    @GeneratedValue
    private Long id;
...
}
 
@Entity(name = "Phone")
public static class Phone {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "`number`")
    private String number;
 
    @ManyToOne
    @JoinColumn(name = "person_id",
            foreignKey = @ForeignKey(name = "PERSON_ID_FK")
    )
    private Person person;
 
}

@OneToMany

Unidirectional @OneToMany
For an unidirectional @OneToMany association, Hibernate uses a link table between the two joining entities.

@Entity(name = "Person")
public static class Person {
 
    @Id
    @GeneratedValue
private Long id;
 
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Phone> phones = new ArrayList<>();
 
    public Person() {
    }
...
}
 
@Entity(name = "Phone")
public static class Phone {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "`number`")
    private String number;
 
    public Phone() {
    }
...
}

Bidirectional @OneToMany
The bidirectional @OneToMany association also requires a @ManyToOne association on the child side. Although the Domain Model exposes two sides to navigate this association, behind the scenes, the relational database has only one foreign key for this relationship.
Every bidirectional association must have one owning side only (the child side), the other one being referred to as the inverse (or the mappedBy) side.

Parent = mappedBy side = inverse side = One side

@Entity(name = "Person")
public static class Person {

    @Id
    @GeneratedValue
    private Long id;
    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Phone> phones = new ArrayList<>();

    public Person() {
}

    public List<Phone> getPhones() {
        return phones;
    }
 
    public void addPhone(Phone phone) {
        phones.add( phone );
        phone.setPerson( this );
    }
 
    public void removePhone(Phone phone) {
        phones.remove( phone );
        phone.setPerson( null );
    }
}

@Entity(name = "Phone")
public static class Phone {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    @Column(name = "`number`", unique = true)
    private String number;

    @ManyToOne
private Person person;

//    @ManyToOne( fetch = FetchType.LAZY )
//    @NotFound ( action = NotFoundAction.IGNORE )
//    @JoinColumn(
//        name = "firstName",
//        referencedColumnName = "first_name",
//        insertable = false,
//        updatable = false
//    private Person person;
    )
}

Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times. The addPhone() and removePhone() are utilities methods that synchronize both ends whenever a child element is added or removed.

The unidirectional associations are not very efficient when it comes to removing child entities. In this particular example, upon flushing the persistence context, Hibernate deletes all database child entries and reinserts the ones that are still found in the in-memory persistence context.
On the other hand, a bidirectional @OneToMany association is much more efficient because the child entity controls the association.

@OneToOne

The @OneToOne association can either be unidirectional or bidirectional. A unidirectional association follows the relational database foreign key semantics, the client-side owning the relationship. A bidirectional association features a mappedBy @OneToOne parent side too.
Unidirectional @OneToOne

@Entity(name = "Phone")
public static class Phone {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "`number`")
    private String number;
 
    @OneToOne
    @JoinColumn(name = "details_id")
    private PhoneDetails details;
 
    public Phone() {
    }
...
}
 
@Entity(name = "PhoneDetails")
public static class PhoneDetails {
 
    @Id
    @GeneratedValue
    private Long id;
 
...
}

Bidirectional @OneToOne

@Entity(name = "Phone")
public static class Phone {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "`number`")
    private String number;
 
    @OneToOne(mappedBy = "phone", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private PhoneDetails details;
 
    public Phone() {
    }
 
 
    public void addDetails(PhoneDetails details) {
        details.setPhone( this );
        this.details = details;
    }
 
    public void removeDetails() {
        if ( details != null ) {
            details.setPhone( null );
            this.details = null;
        }
    }
}
 
@Entity(name = "PhoneDetails")
public static class PhoneDetails {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String provider;
 
    private String technology;
 
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "phone_id")
    private Phone phone;
 
    public PhoneDetails() {
    }
}

 @ManyToMany

Unidirectional @ManyToMany
Entity(name = "Person")
public static class Person {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private List<Address> addresses = new ArrayList<>();
 
    public Person() {
    }
 
}
 
@Entity(name = "Address")
public static class Address {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String street;
 
    @Column(name = "`number`")
    private String number;
 
    public Address() {
    }
 
}

For @ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.
For example, if @ManyToMany(cascade = CascadeType.ALL) was defined and the first person would be deleted, Hibernate would throw an exception because another person is still associated with the address that’s being deleted.

Bidirectional @ManyToMany
@Entity(name = "Person")
public static class Person {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @NaturalId
private String registrationNumber;
 
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private List<Address> addresses = new ArrayList<>();
 
    public Person() {
    }
 
    public List<Address> getAddresses() {
        return addresses;
    }
 
    public void addAddress(Address address) {
        addresses.add( address );
        address.getOwners().add( this );
    }
 
    public void removeAddress(Address address) {
        addresses.remove( address );
        address.getOwners().remove( this );
    }
}
 
@Entity(name = "Address")
public static class Address {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "`number`")
    private String number;
 
    @ManyToMany(mappedBy = "addresses")
    private List<Person> owners = new ArrayList<>();
 
    public Address() {
    }
 
 
}

If a bidirectional @OneToMany association performs better when removing or changing the order of child elements, the @ManyToMany relationship cannot benefit from such an optimization because the foreign key side is not in control. To overcome this limitation, the link table must be directly exposed and the @ManyToMany association split into two bidirectional @OneToMany relationships.
Bidirectional many-to-many with a link entity
To most natural @ManyToMany association follows the same logic employed by the database schema, and the link table has an associated entity which controls the relationship for both sides that need to be joined.
@Entity(name = "Person")
public static class Person implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String registrationNumber;

    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<PersonAddress> addresses = new ArrayList<>();

    public Person() {
    }

    public List<PersonAddress> getAddresses() {
        return addresses;
    }

    public void addAddress(Address address) {
        PersonAddress personAddress = new PersonAddress( this, address );
        addresses.add( personAddress );
        address.getOwners().add( personAddress );
    }

    public void removeAddress(Address address) {
        PersonAddress personAddress = new PersonAddress( this, address );
        address.getOwners().remove( personAddress );
        addresses.remove( personAddress );
        personAddress.setPerson( null );
        personAddress.setAddress( null );
    }

}

@Entity(name = "PersonAddress")
public static class PersonAddress implements Serializable {

    @Id
    @ManyToOne
    private Person person;

    @Id
    @ManyToOne
    private Address address;

}

@Entity(name = "Address")
public static class Address implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    private String street;

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

    private String postalCode;

    @OneToMany(mappedBy = "address", cascade = CascadeType.ALL, orphanRemoval = true) // no set method for this field!
    private List<PersonAddress> owners = new ArrayList<>();

    public Address() {
    }

    public Address(String street, String number, String postalCode) {
        this.street = street;
        this.number = number;
        this.postalCode = postalCode;
    }

    public Long getId() {
        return id;
    }

    public String getStreet() {
        return street;
    }

    public String getNumber() {
        return number;
    }

    public String getPostalCode() {
        return postalCode;
    }

    public List<PersonAddress> getOwners() {
        return owners;
    }

    @Override
    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }
        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }
        Address address = (Address) o;
        return Objects.equals( street, address.street ) &&
                Objects.equals( number, address.number ) &&
                Objects.equals( postalCode, address.postalCode );
    }

    @Override
    public int hashCode() {
        return Objects.hash( street, number, postalCode );
    }
}

Collections of Value Types

@Entity(name = "Person")
public static class Person {
 
    @Id
    private Long id;
 
    @ElementCollection
    @OrderColumn(name = "order_id")
    private List<Phone> phones = new ArrayList<>();
}
 
@Embeddable
public static class Phone {
}

Bags, Ordered Lists, Sets, Sorted Sets

@Entity(name = "Person")
public static class Person {
 
    @Id
    private Long id;
    @OneToMany(cascade = CascadeType.ALL)
//    @OrderBy("number") // for Ordered Lists
private List<Phone> phones = new ArrayList<>();
//    private Set<Phone> phones = new HashSet<>(); // for Sets
//    @SortNatural // for Sorted Sets
//    private SortedSet<Phone> phones = new TreeSet<>();
 
}
 
@Entity(name = "Phone")
public static class Phone {
 
}

Arrays

Hibernate does support the mapping of arrays in the Java domain model - conceptually the same as mapping a List. However, it is important to realize that it is impossible for Hibernate to offer lazy-loading for arrays of entities and, for this reason, it is strongly recommended to map a "collection" of entities using a List rather than an array.

Maps

public enum PhoneType {
    LAND_LINE,
    MOBILE
}
 
@Entity(name = "Person")
public static class Person {
 
    @Id
    private Long id;
 
    @Temporal(TemporalType.TIMESTAMP)
    @ElementCollection
    @CollectionTable(name = "phone_register")
    @Column(name = "since")
    @MapKeyJoinColumn(name = "phone_id", referencedColumnName = "id")
    private Map<Phone, Date> phoneRegister = new HashMap<>();
 
    public Person() {}
 
    public Person(Long id) {
        this.id = id;
    }
 
    public Map<Phone, Date> getPhoneRegister() {
        return phoneRegister;
    }
}
 
@Embeddable
public static class Phone {
 
}
 
create table call_register (

    person_id int8 not null,

    phone_number int4,
    call_timestamp_epoch int8 not null,
    primary key (person_id, call_key)
)
 


@Entity
@Table(name = "person")
public static class Person {
 
    @Id
    private Long id;
 
    @ElementCollection
    @CollectionTable(
        name = "call_register",
        joinColumns = @JoinColumn(name = "person_id")
    )
    @MapKeyType(
        @Type(
            type = "org.hibernate.userguide.collections.type.TimestampEpochType"
        )
    )
    @MapKeyColumn( name = "call_timestamp_epoch" )
    @Column(name = "phone_number")
    private Map<Date, Integer> callRegister = new HashMap<>();
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public Map<Date, Integer> getCallRegister() {
        return callRegister;
    }
}
Unidirectional maps
A unidirectional map exposes a parent-child association from the parent-side only.
public enum PhoneType {
    LAND_LINE,
    MOBILE
}

@Entity(name = "Person")
public static class Person {

    @Id
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinTable(
            name = "phone_register",
            joinColumns = @JoinColumn(name = "phone_id"),
            inverseJoinColumns = @JoinColumn(name = "person_id"))
    @MapKey(name = "since")
    @MapKeyTemporal(TemporalType.TIMESTAMP)
    private Map<Date, Phone> phoneRegister = new HashMap<>();

}

@Entity(name = "Phone")
public static class Phone {

}
Bidirectional maps
@Entity(name = "Person")
public static class Person {

    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
    @MapKey(name = "type")
    @MapKeyEnumerated
    private Map<PhoneType, Phone> phoneRegister = new HashMap<>();

}

@Entity(name = "Phone")
public static class Phone {
    @ManyToOne
    private Person person;

}