Persistence

Feb 4th, 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

Changing Persistence Unit dynamically

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

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

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

@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

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

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

Json converter

Data model

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

@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

@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:

  @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);
  }

}