Java Persistence API
The Java Persistence API (JPA) is a specification of Java and it is a standard technology. It is used to persist data between Java object and relational database. JPA acts as a bridge between object-oriented domain models and relational database systems. As JPA is just a specification, it doesn't perform any operation by itself. By itself, JPA is not a tool or framework; rather, it defines a set of concepts that can be implemented by any tool or framework. While JPA's object-relational mapping (ORM) model was originally based on Hibernate, it has since evolved.
JPA vs Hibernate
JPA is a standard, while Hibernate is not. In hibernate, we use Session for handling the persistence of data, while in JPA, we use Entity Manager. The query language in Hibernate is Hibernate Query language, while in JPA, the query language is Java Persistence query language. Hibernate is one of the most JPA providers.
Spring Data JPA
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
It provides the following key dependencies:
- Hibernate: One of the most popular JPA implementations.
- Spring Data JPA: Helps you to implement JPA-based repositories.
- Spring ORM: Core ORM support from the Spring Framework.
Source: https://spring.io/projects/spring-data-jpa
Spring and EclipseLink
By default Spring uses Hibernate as the default JPA vendor. Although Hibernate is a good choice, someone may prefers to use EclipseLink as it was supposed to be the reference implementation for the Java Persistence JSR.
How configuring Spring-Boot to use EclipseLink as the JPA provider: https://blog.marcnuri.com/spring-data-jpa-eclipselink-configuring-spring-boot-to-use-eclipselink-as-the-jpa-provider/
Jakarta Persistence
Jakarta Persistence defines a standard for management of persistence and object/relational mapping in Java(R) environments.
Connection Pools
A connection pool is a cache of database connections maintained so that the connections can be reused when future requests to the database are required. Connection pools are used to enhance the performance of executing commands on a database. Opening and maintaining a database connection for each user, especially requests made to a dynamic database-driven website application, is costly and wastes resources. In connection pooling, after a connection is created, it is placed in the pool and it is used again so that a new connection does not have to be established. If all the connections are being used, a new connection is made and is added to the pool. Connection pooling also cuts down on the amount of time a user must wait to establish a connection to the database.
HikariCP
HikariCP is a very lightweight (at roughly 130Kb) and lightning-fast JDBC connection pooling framework developed by Brett Wooldridge around 2012. It is a default in SpringBoot.
HikariCP-benchmark: https://github.com/brettwooldridge/HikariCP-benchmark
Transaction management
Transaction management refers to the tasks of processing multiple transactions issued by various clients of a database server in such a way that the ACID contract can be fulfilled, that is, the properties of atomicity, consistency preservation, isolation, and durability of each individual transaction can be guaranteed. Transaction management is generally understood as requiring serializability-based concurrency control as well as recovery from failures. Concurrency control is the task of scheduling transactions such that their serializability can be guaranteed, while recovery has to restore a consistent database state after a system or media failure. Assuming that the database server is in charge of the “C,” the former guarantees the “I” in ACID, the latter the “A” and “D” properties. Transaction management has to be highly efficient, as modern transaction servers need to accommodate thousands of transactions...
...
Java Persistence API
The Java Persistence API (JPA) is a specification of Java and it is a standard technology. It is used to persist data between Java object and relational database. JPA acts as a bridge between object-oriented domain models and relational database systems. As JPA is just a specification, it doesn't perform any operation by itself. By itself, JPA is not a tool or framework; rather, it defines a set of concepts that can be implemented by any tool or framework. While JPA's object-relational mapping (ORM) model was originally based on Hibernate, it has since evolved.
JPA vs Hibernate
JPA is a standard, while Hibernate is not. In hibernate, we use Session for handling the persistence of data, while in JPA, we use Entity Manager. The query language in Hibernate is Hibernate Query language, while in JPA, the query language is Java Persistence query language. Hibernate is one of the most JPA providers.
Spring Data JPA
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
It provides the following key dependencies:
- Hibernate: One of the most popular JPA implementations.
- Spring Data JPA: Helps you to implement JPA-based repositories.
- Spring ORM: Core ORM support from the Spring Framework.
Source: https://docs.spring.io/projects/spring-framework/docs/5.3.7/reference/html/data-access.html#transaction
Aspect-Oriented Programming
Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)
One of the key components of Spring is the AOP framework. While the Spring IoC (Inversion of control) container does not depend on AOP, meaning you do not need to use AOP if you don’t want to, AOP complements Spring IoC to provide a very capable middleware solutiondata-jpa
Spring and EclipseLink
By default Spring uses Hibernate as the default JPA vendor. Although Hibernate is a good choice, someone may prefers to use EclipseLink as it was supposed to be the reference implementation for the Java Persistence JSR.
How configuring Spring-Boot to use EclipseLink as the JPA provider: https://blog.marcnuri.com/spring-data-jpa-eclipselink-configuring-spring-boot-to-use-eclipselink-as-the-jpa-provider/
Jakarta Persistence
Jakarta Persistence defines a standard for management of persistence and object/relational mapping in Java(R) environments.
Connection Pools
A connection pool is a cache of database connections maintained so that the connections can be reused when future requests to the database are required. Connection pools are used to enhance the performance of executing commands on a database. Opening and maintaining a database connection for each user, especially requests made to a dynamic database-driven website application, is costly and wastes resources. In connection pooling, after a connection is created, it is placed in the pool and it is used again so that a new connection does not have to be established. If all the connections are being used, a new connection is made and is added to the pool. Connection pooling also cuts down on the amount of time a user must wait to establish a connection to the database.
HikariCP
HikariCP is a very lightweight (at roughly 130Kb) and lightning-fast JDBC connection pooling framework developed by Brett Wooldridge around 2012. It is a default in SpringBoot.
HikariCP-benchmark: https://github.com/brettwooldridge/HikariCP-benchmark
Transaction management
Transaction management refers to the tasks of processing multiple transactions issued by various clients of a database server in such a way that the ACID contract can be fulfilled, that is, the properties of atomicity, consistency preservation, isolation, and durability of each individual transaction can be guaranteed. Transaction management is generally understood as requiring serializability-based concurrency control as well as recovery from failures. Concurrency control is the task of scheduling transactions such that their serializability can be guaranteed, while recovery has to restore a consistent database state after a system or media failure. Assuming that the database server is in charge of the “C,” the former guarantees the “I” in ACID, the latter the “A” and “D” properties. Transaction management has to be highly efficient, as modern transaction servers need to accommodate thousands of transactions...
The Spring Framework provides a consistent abstraction for transaction management. The Spring Framework’s declarative transaction management is made possible with Spring aspect-oriented programming (AOP), although, as the transactional aspects code comes with the Spring Framework distribution and may be used in a boilerplate fashion, AOP concepts do not generally have to be understood to make effective use of this code.
Source: https://docs.spring.io/spring-framework/docs/5.3.7/reference/html/data-access.html#transaction
Aspect-Oriented Programming
Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)
One of the key components of Spring is the AOP framework. While the Spring IoC (Inversion of control) container does not depend on AOP, meaning you do not need to use AOP if you don’t want to, AOP complements Spring IoC to provide a very capable middleware solution.
Source: https://docs.spring.io/spring-framework/docs/5.3.7/reference/html/core.html#aop
Use a Higher-level Database Migration Tool
Spring Boot supports two higher-level migration tools: Flyway and Liquibase.
Example how how to set things up with Liquibase: https://github.com/spring-projects/spring-boot/tree/v2.1.18.RELEASE/spring-boot-samples/spring-boot-sample-liquibase
Policy Framework - Models
Models are implemented using Jakarta Persistence. Spring Data JPA needs same implementation.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Table(name = "ToscaProperty")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Data
@EqualsAndHashCode(callSuper = false)
public class JpaToscaProperty extends PfConcept implements PfAuthorative<ToscaProperty> {
private static final long serialVersionUID = 1675770231921107988L;
@EmbeddedId
@VerifyKey
@NotNull
private PfReferenceKey key;
@Column
@VerifyKey
@NotNull
private PfConceptKey type;
@Column
@NotBlank
private String description;
|
Spring Repository
With Spring Data JPA, is a good choice to implement repositories.
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
Source: https://docs.spring.io/spring-framework/docs/5.3.7/reference/html/core.html#aop
Use a Higher-level Database Migration Tool
Spring Boot supports two higher-level migration tools: Flyway and Liquibase.
Example how how to set things up with Liquibase: https://github.com/spring-projects/spring-boot/tree/v2.1.18.RELEASE/spring-boot-samples/spring-boot-sample-liquibase
Policy Framework - Models
Models are implemented using Jakarta Persistence. Spring Data JPA needs same implementation.
...
language | java |
---|---|
title | JpaToscaProperty |
linenumbers | true |
...
io/spring-data/data-commons/docs/2.5.1/reference/html/#repositories
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Repository
public interface JpaToscaPropertyRepository extends JpaRepository<JpaToscaProperty, PfReferenceKey> {
}
|
and also it needs to implement a JUnit test
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@ExtendWith(SpringExtension.class) @DataJpaTest @Import(value = ParticipantPolicyParameters.class) @TestPropertySource(locations = {"classpath:application_test.properties"}) class JpaToscaPropertyRepositoryTest { @Autowired private JpaToscaPropertyRepository toscaPropertyRepository; @Test void test() { JpaToscaProperty toscaProperty = new JpaToscaProperty(); PfReferenceKey key = toscaProperty.getKey(); Map<String, String> metadata = new HashMap<>(); metadata.put("Key", "Value"); metadata.put("K", "V"); List<JpaToscaConstraint> constraints = new ArrayList<>(); @EmbeddedId String[] list @VerifyKey= new String[] {"First", "Second"}; @NotNull private PfReferenceKey key; constraints.add(new JpaToscaConstraintValidValues(Stream.of(list).collect(Collectors.toList()))); @Column @VerifyKey toscaProperty.setDefaultValue("DefaultValue"); @NotNull private PfConceptKey typetoscaProperty.setDescription("Description"); @Column @NotBlank toscaProperty.setRequired(true); private String description; |
Spring Repository
With Spring Data JPA, is a good choice to implement repositories.
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
Source: https://docs.spring.io/spring-data/data-commons/docs/2.5.1/reference/html/#repositories
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Repository
public interface JpaToscaPropertyRepository extends JpaRepository<JpaToscaProperty, PfReferenceKey> {
}
|
and also it needs to implement a JUnit test
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@ExtendWith(SpringExtension.class) @DataJpaTest @Import(value = ParticipantPolicyParameters.class) @TestPropertySource(locations = {"classpath:application_test.properties"}) class JpaToscaPropertyRepositoryTest { @Autowired private JpaToscaPropertyRepository toscaPropertyRepository toscaProperty.setStatus(ToscaProperty.Status.EXPERIMENTAL); toscaProperty.setMetadata(metadata); toscaProperty.setConstraints(constraints); toscaPropertyRepository.save(toscaProperty); Optional<JpaToscaProperty> opt = toscaPropertyRepository.findById(key); assertThat(opt).isNotEmpty(); JpaToscaProperty actual = opt.get(); assertThat(actual.getDefaultValue()).isEqualTo(toscaProperty.getDefaultValue()); assertThat(actual.getDescription()).isEqualTo(toscaProperty.getDescription()); assertThat(actual.isRequired()).isEqualTo(toscaProperty.isRequired()); @Test void test() {assertThat(actual.getStatus()).isEqualTo(toscaProperty.getStatus()); JpaToscaProperty toscaProperty = new JpaToscaProperty()assertThat(actual.getType()).isEqualTo(toscaProperty.getType()); PfReferenceKey key = toscaProperty.getKey(assertThat(actual.getConstraints()).isEqualTo(toscaProperty.getConstraints()); Map<String, String> metadata = new HashMap<>(); metadata.put("Key", "Value"); metadata.put("K", "V"); } } |
Using Dao
Dao objects are already implemented in Policy Framework, and there is a way to use them and avoid to create repositories, but It needs to create a new DefaultPfDao class that uses the Entity Manger with no "begin transaction" and no "commit".
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
public class DefaultPfDao implements PfDao { private static final List<JpaToscaConstraint>Logger constraintsLOGGER = new ArrayList<>(LoggerFactory.getLogger(DefaultPfDao.class); private final String[] list = new String[] {"First", "Second"}; EntityManager entityManager; /** * Constructor. * constraints.add(new JpaToscaConstraintValidValues(Stream.of(list).collect(Collectors.toList()))); * @param entityManager EntityManager toscaProperty.setDefaultValue("DefaultValue"); */ public toscaProperty.setDescription("Description");DefaultPfDao(EntityManager entityManager) { toscaProperty.setRequired(true)this.entityManager = entityManager; } toscaProperty.setStatus(ToscaProperty.Status.EXPERIMENTAL); @Override public void toscaProperty.setMetadata(metadata); toscaProperty.setConstraints(constraints);init(final DaoParameters daoParameters) throws PfModelException { // Not need toscaPropertyRepository.save(toscaProperty); } Optional<JpaToscaProperty>@Override opt = toscaPropertyRepository.findById(key); public <T extends PfConcept> void create(final T assertThat(opt).isNotEmpty();obj) { JpaToscaPropertyif actual(obj == opt.get();null) { assertThat(actual.getDefaultValue()).isEqualTo(toscaProperty.getDefaultValue()); return; assertThat(actual.getDescription()).isEqualTo(toscaProperty.getDescription()); } assertThat(actualentityManager.isRequiredmerge()).isEqualTo(toscaProperty.isRequired()); obj); } assertThat(actual.getStatus()).isEqualTo(toscaProperty.getStatus());@Override public <T extends PfConcept> assertThat(actual.getType()).isEqualTo(toscaProperty.getType());void delete(final T obj) { assertThatentityManager.remove(actualentityManager.getConstraintscontains()).isEqualTo(toscaProperty.getConstraints(obj) ? obj : entityManager.merge(obj)); } } |
Using Dao
...
|
EntityManager is not thread safe, and it need to use @PersistenceContext annotation, so Spring will inject a thread-safe proxy for the actual transactional EntityManager.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Service @Transactional public class DefaultPfDaoPolicyModelsProviderImpl implements PfDaoPolicyModelsProvider { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPfDao.class); @PersistenceContext private final EntityManager entityManager; /**@Override public * Constructor. ToscaServiceTemplate createServiceTemplate(@NonNull ToscaServiceTemplate serviceTemplate) { * * @param entityManager EntityManagerLOGGER.debug("->createServiceTemplate: serviceTemplate={}", serviceTemplate); */ try { public DefaultPfDao(EntityManager entityManager) { this.entityManager = entityManager; ToscaServiceTemplate createdServiceTemplate = } @Override public void init(final DaoParameters daoParameters) throws PfModelException { new SimpleToscaProvider().appendToServiceTemplate(new DefaultPfDao(entityManager), // Not need } @Override public <T extends PfConcept> void create(final T obj) {new JpaToscaServiceTemplate(serviceTemplate)).toAuthorative(); if (obj == null) {LOGGER.debug("<-createServiceTemplate: createdServiceTemplate={}", createdServiceTemplate); return returncreatedServiceTemplate; } catch entityManager.merge(obj); (Exception pfme) { } @Override throw public <T extends PfConcept> void delete(final T obj) { new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, pfme.getMessage(), pfme); entityManager.remove(entityManager.contains(obj) ? obj : entityManager.merge(obj)); } } |
...
Spring Service
Example how to convert ControlLoopInstantiationProvider class in Spring style using "@Service" and "@Transactional"
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Service @Transactional public class PolicyModelsProviderImpl implements PolicyModelsProviderControlLoopInstantiationProvider { private @PersistenceContextfinal private EntityManager entityManagerControlLoopProvider controlLoopProvider; @Override public ToscaServiceTemplate createServiceTemplate(@NonNull ToscaServiceTemplate serviceTemplate) { LOGGER.debug("->createServiceTemplate: serviceTemplate={}", serviceTemplate)private final CommissioningProvider commissioningProvider; /** try { * Create a instantiation provider. * ToscaServiceTemplate createdServiceTemplate = * @param databaseProviderParameters the parameters for database access */ public new SimpleToscaProvider().appendToServiceTemplate(new DefaultPfDao(entityManager),ControlLoopInstantiationProvider(ControlLoopProvider controlLoopProvider, CommissioningProvider commissioningProvider) { this.controlLoopProvider = controlLoopProvider; new JpaToscaServiceTemplate(serviceTemplate)).toAuthorative(); this.commissioningProvider = commissioningProvider; } LOGGER.debug("<-createServiceTemplate: createdServiceTemplate={}", createdServiceTemplate); @Transactional public InstantiationResponse createControlLoops(ControlLoops controlLoops) throws PfModelException { return createdServiceTemplate; for (ControlLoop }controlLoop catch: (Exception pfmecontrolLoops.getControlLoopList()) { ControlLoop throwcheckControlLoop new= PfModelRuntimeExceptioncontrolLoopProvider.getControlLoop(ResponsecontrolLoop.Status.INTERNAL_SERVER_ERROR, pfme.getMessage(), pfme); getKey().asIdentifier()); } } |
Spring Service
Example how to convert ControlLoopInstantiationProvider class in Spring style using "@Service" and "@Transactional"
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Service public class ControlLoopInstantiationProvider { if (checkControlLoop != null) { private final ControlLoopProvider controlLoopProvider; throw new PfModelException(Response.Status.BAD_REQUEST, private final CommissioningProvider commissioningProvider; /** * Create a instantiation provider. controlLoop.getKey().asIdentifier() + " already defined"); * * @param databaseProviderParameters the} parameters for database access } */ public ControlLoopInstantiationProvider(ControlLoopProvider controlLoopProvider, BeanValidationResult validationResult = validateControlLoops(controlLoops); CommissioningProvider commissioningProviderif (!validationResult.isValid()) { this.controlLoopProvider = controlLoopProvider; throw this.commissioningProvider = commissioningProvidernew PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult()); } } @Transactional public InstantiationResponse controlLoopProvider.createControlLoops(ControlLoops controlLoops.getControlLoopList()); throws PfModelException { InstantiationResponse response for= (ControlLoop controlLoop : controlLoops.getControlLoopList()) {new InstantiationResponse(); ControlLoop checkControlLoop = controlLoopProvider.getControlLoop(controlLoop.getKeyresponse.setAffectedControlLoops(controlLoops.getControlLoopList().asIdentifierstream()); if .map(checkControlLoopcl != null) {-> cl.getKey().asIdentifier()).collect(Collectors.toList())); return response; throw new PfModelException(Response.Status.BAD_REQUEST, controlLoop.getKey().asIdentifier() + " already defined"); } } BeanValidationResult validationResult = validateControlLoops(controlLoops); if (!validationResult.isValid()) { throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult()); } controlLoopProvider.createControlLoops(controlLoops.getControlLoopList()); InstantiationResponse response = new InstantiationResponse(); response.setAffectedControlLoops(controlLoops.getControlLoopList().stream() .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList())); return response; } } |
Transaction
Transaction Propagation in SpringBoot:
- REQUIRED is the default propagation. Spring checks if there is an active transaction, then it creates a new one if nothing existed;
- For SUPPORTS, Spring first checks if an active transaction exists. If a transaction exists, then the existing transaction will be used. If there isn't a transaction, it is executed non-transactional;
- When the propagation is MANDATORY, if there is an active transaction, then it will be used. If there isn't an active transaction, then Spring throws an exception;
- For transactional logic with NEVER propagation, Spring throws an exception if there's an active transaction;
- NOT_SUPPORTED, Spring at first suspends the current transaction if it exists, then the business logic is executed without a transaction;
- When the propagation is REQUIRES_NEW, Spring suspends the current transaction if it exists and then creates a new one.
Read only Transaction
If the transaction is effectively read-only, it allows for corresponding optimizations at runtime. (https://stackoverflow.com/questions/1614139/spring-transactional-read-only-propagation)
Note: In Spring, @Transactional
works by creating a proxy of the class and intercepting the annotated method (AOP Proxies). This means that @Transactional
doesn't work if you are calling the annotated method from another method of the same class.
Other links
- https://gerrit.nordix.org/c/local/onap/clamp-pilot/+/8929
- https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
- https://docs.spring.io/spring-framework/docs/current/reference/html/
- https://www.adam-bien.com/roller/abien/entry/jpa_ejb3_killed_the_dao
- https://www.adam-bien.com/roller/abien/entry/daos_aren_t_dead_but
...