Persistence
February 4, 2018

Java Type to JDBC Type

JAVA TYPE JDBC TYPE
java.time.LocalDate DATE
java.time.LocalTime TIME
java.time.LocalDateTime TIMESTAMP
java.time.OffsetTime TIME_WITH_TIMEZONE
java.time.OffsetDateTime TIMESTAMP_WITH_TIMEZONE
java.time.Duration BIGINT
java.time.Instant TIMESTAMP
java.time.ZonedDateTime TIMESTAMP

SequenceGenerator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Entity
public class Project implements Serializable {
 
  @Id
  @GeneratedValue(generator = "seq_project_id", strategy = GenerationType.SEQUENCE)
  @SequenceGenerator(name= "seq_project_id", sequenceName = "seq_project_id", allocationSize = 1, initialValue = 10000001)
  private Long id;

  ...
}

Changing Persistence Unit dynamically

PS: Persistence.createEntityManagerFactory() is for unmanaged environments (Java SE), not for managed environments (Java EE).

Keep the persistence unit file (Persistence.xml) as it’s. You can override the properties in it as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
EntityManagerFactory managerFactory = null;
Map<String, String> persistenceMap = new HashMap<String, String>();

persistenceMap.put("javax.persistence.jdbc.url", "<url>");
persistenceMap.put("javax.persistence.jdbc.user", "<username>");
persistenceMap.put("javax.persistence.jdbc.password", "<password>");
persistenceMap.put("javax.persistence.jdbc.driver", "<driver>");

managerFactory = Persistence.createEntityManagerFactory("<current persistence unit>", persistenceMap);
manager = managerFactory.createEntityManager();

Bulk update with JPA CriteriaUpdate

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public void bulkUpdatePricesInLocal() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaUpdate<ProductEntity> cu = cb.createCriteriaUpdate(ProductEntity.class);
    Root<ProductEntity> root = cu.from(ProductEntity.class);
    Join<ProductEntity, CurrencyEntity> currencyToConsumer = root.join(ProductEntity_.currencyToConsumer);
    Join<ProductEntity, CurrencyEntity> currencyToRetailer = root.join(ProductEntity_.currencyToRetailer);

    Expression<BigDecimal> prodConsumer = cb.prod(root.<BigDecimal>get(ProductEntity_.priceToConsumer), currencyToConsumer.<BigDecimal>get(CurrencyEntity_.rateTCMB));
    Expression<BigDecimal> prodRetailer = cb.prod(root.<BigDecimal>get(ProductEntity_.priceToRetailer), currencyToRetailer.<BigDecimal>get(CurrencyEntity_.rateTCMB));

    cu.set(root.get(ProductEntity_.priceToConsumerInLocal), prodConsumer);
    cu.set(root.get(ProductEntity_.priceToRetailerInLocal), prodRetailer);

    Query query = em.createQuery(cu);
    int resultList = query.executeUpdate();
}

Many-many relationship with extra columns in JPA

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@Embeddable
public class DeveloperProjectId implements Serializable {
 
    private Long developerId;
    private Long projectId;

    // constructor, getters, setters
}

@Entity
public class DeveloperProject implements Serializable {
 
    @EmbeddedId
    private DeveloperProjectId developerProjectId;
 
    @ManyToOne
    @MapsId("developerId")
    private Developer developer;
 
    @ManyToOne
    @MapsId("projectId")
    private Project project;
 
    @Column
    private String task;

    // constructor, getters, setters
}

@Entity
public class Developer implements Serializable {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
 
    @OneToMany(mappedBy = "developer")
    private List<DeveloperProject> projects;

    // constructor, getters, setters
}

@Entity
public class Project implements Serializable {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column
    private String name;
 
    @OneToMany(mappedBy = "project")
    private List<DeveloperProject> developers;

    // constructor, getters, setters
}

CriteriaBuilder join two tables with a custom condition

1
2
3
4
CriteriaQuery<A> searchQuery = criteriaBuilder.createQuery(A.class);
Root<A> aRoot = searchQuery.from(A.class);
Join<A, B> bJoin= aRoot.join("mappedB", JoinType.LEFT);
bJoin.on(criteriaBuilder.equal(bJoin.get("idLanguage"), 22));

equal to

1
LEFT OUTER JOIN B ON A.ID = B.A_ID AND B.idLanguage = 22

Json converter

Data model

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyJson implements Serializable {

  private String stringProp;

  private Long longProp;

  public String getStringProp() {
    return stringProp;
  }

  public void setStringProp(String stringProp) {
    this.stringProp = stringProp;
  }

  public Long getLongProp() {
    return longProp;
  }

  public void setLongProp(Long longProp) {
    this.longProp = longProp;
  }
}

JsonAttributeConverter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Converter(autoApply = true)
public class JsonAttributeConverter implements AttributeConverter<Object, String> {

  private JsonbConfig cfg = new JsonbConfig().withFormatting(true);

  private Jsonb jsonb = JsonbBuilder.create(cfg);

  @Override
  public String convertToDatabaseColumn(Object object) {
    if (object == null) {
      return null;
    }

    return jsonb.toJson(object);
  }

  @Override
  public Object convertToEntityAttribute(String value) {
    if (value == null) {
      return null;
    }

    return jsonb.fromJson(value, value.getClass());
  }

}

JPA Entity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Entity
public class TestEntity implements Serializable {
  
  ...
  
  @Column(columnDefinition = "JSON")
  @Convert(converter = JsonAttributeConverter.class)
  private MyJson jsonData;

  ...

}

@Mutable annotation

Ref: @Mutable

Don’t forget to annotate complex fiels with the @Mutable annotation. The change-tracking mechanism only works for non-mutable/basic fields. And thus, no updates were detected.

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  @Column(columnDefinition = "JSON")
  @Convert(converter = RehearsalAttributeConverter.class)
  @Mutable
  private Rehearsal rehearsal;
  ...


public class Rehearsal implements Serializable {
  private List<Product> products;
  private Map<Product, Feature> features;
  private double shortTermDebt = 0;
  ...
}

@Converter(autoApply = true)
public class RehearsalAttributeConverter implements AttributeConverter<Rehearsal, String> {

  private JsonbConfig cfg = new JsonbConfig().withFormatting(true);

  private Jsonb jsonb = JsonbBuilder.create(cfg);

  @Override
  public String convertToDatabaseColumn(Rehearsal object) {
    if (object == null) {
      return null;
    }

    return jsonb.toJson(object, Rehearsal.class);
  }

  @Override
  public Rehearsal convertToEntityAttribute(String value) {
    if (value == null) {
      return null;
    }

    return jsonb.fromJson(value, Rehearsal.class);
  }

}

ElementCollection Map<Integer, Entity>

Association annotated with @ElementCollection is valid for the following mapping types:

  • Map<Basic,Basic>
  • Map<Basic,Embeddable>
  • Map<Embeddable,Basic>
  • Map<Embeddable,Embeddable>
  • Map<Entity,Basic>
  • Map<Entity,Embeddable>

Association annotated with @OneToMany / @ManyToMany is valid for the following mapping types:

  • Map<Basic,Entity>
  • Map<Embeddable,Entity>
  • Map<Entity,Entity>

Inheritance Mapping

  • MappedSuperclass – the parent classes, can’t be entities
  • Single Table – The entities from different classes with a common ancestor are placed in a single table.
  • Joined Table – Each class has its table, and querying a subclass entity requires joining the tables.
  • Table per Class – All the properties of a class are in its table, so no join is required.

MappedSuperclass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@MappedSuperclass
public class Employee {

    @Id
    private long id;
    private String name;

    // constructor, getters, setters
}

@Entity
public class FullTimeEmployee extends Employee {
    private int salary;
    // constructor, getters, setters 
}

@Entity
public class PartTimeEmployee extends Employee {
    private int hourlyRate;
    // constructor, getters, setters 
}
    classDiagram
    class FullTimeEmployee{
      +long id
      +String name
      +int salary
      +getId()
      +getName()
      +getSalary()
    }
    class PartTimeEmployee{
      +long id
      +String name
      +int hourlyRate
      +getId()
      +getName()
      +getHourlyRate()
    }
  

Single Table:

to differentiate between the records, by default, it is done through a discriminator column called DTYPE. To customize the discriminator column, we can use the @DiscriminatorColumn annotation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Employee {

    @Id
    private long id;
    private String name;

    // constructor, getters, setters
}

@Entity
public class FullTimeEmployee extends Employee {
    private int salary;
    // constructor, getters, setters 
}

@Entity
public class PartTimeEmployee extends Employee {
    private int hourlyRate;
    // constructor, getters, setters 
}
    classDiagram
    class Employee{
      +long id
      +int dtype
      +String name
      +int salary
      +int hourlyRate
      +getId()
      +getName()
      +getSalary()
      +getHourlyRate()
    }
  

Joined Table:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Employee {

    @Id
    private long id;
    private String name;

    // constructor, getters, setters
}

@Entity
public class FullTimeEmployee extends Employee {
    private int salary;
    // constructor, getters, setters 
}

@Entity
public class PartTimeEmployee extends Employee {
    private int hourlyRate;
    // constructor, getters, setters 
}
    classDiagram
    Employee <|-- FullTimeEmployee
    Employee <|-- PartTimeEmployee
    Employee : +long id
    Employee : +int dtype
    Employee : +String name
    Employee: +getId()
    Employee: +getName()
    class FullTimeEmployee{
      +int salary
      +getSalary()
    }
    class PartTimeEmployee{
      +int hourlyRate
      +getHourlyRate()
    }
  

Table per Class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Employee {

    @Id
    private long id;
    private String name;

    // constructor, getters, setters
}

@Entity
public class FullTimeEmployee extends Employee {
    private int salary;
    // constructor, getters, setters 
}

@Entity
public class PartTimeEmployee extends Employee {
    private int hourlyRate;
    // constructor, getters, setters 
}
    classDiagram
    Employee <|-- FullTimeEmployee
    Employee <|-- PartTimeEmployee
    Employee : +long id
    Employee : +int dtype
    Employee : +String name
    Employee: +getId()
    Employee: +getName()
    class FullTimeEmployee{
      +int salary
      +getSalary()
    }
    class PartTimeEmployee{
      +int hourlyRate
      +getHourlyRate()
    }