...
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/
...
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
Using With Spring Data JPA it needs , is a good choice to implement a repository for each modelrepositories.
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.
...
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<>(); String[] list = new String[] {"First", "Second"}; constraints.add(new JpaToscaConstraintValidValues(Stream.of(list).collect(Collectors.toList()))); toscaProperty.setDefaultValue("DefaultValue"); toscaProperty.setDescription("Description"); toscaProperty.setRequired(true); 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()); assertThat(actual.getStatus()).isEqualTo(toscaProperty.getStatus()); assertThat(actual.getType()).isEqualTo(toscaProperty.getType()); assertThat(actual.getConstraints()).isEqualTo(toscaProperty.getConstraints()); } } |
Spring Service
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".Example how to convert ControlLoopInstantiationProvider class in Spring style using "@Service" and "@Transactional"
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Service public class DefaultPfDao implements ControlLoopInstantiationProviderPfDao { private static final ControlLoopProvider controlLoopProvider;Logger LOGGER = LoggerFactory.getLogger(DefaultPfDao.class); private final CommissioningProviderEntityManager commissioningProviderentityManager; /** * Create a instantiation providerConstructor. * * @param databaseProviderParameters the parameters for database accessentityManager EntityManager */ public ControlLoopInstantiationProviderDefaultPfDao(ControlLoopProvider controlLoopProvider, CommissioningProvider commissioningProviderEntityManager entityManager) { this.controlLoopProviderentityManager = controlLoopProviderentityManager; } this.commissioningProvider = commissioningProvider;@Override } public void init(final @Transactional public InstantiationResponse createControlLoops(ControlLoops controlLoopsDaoParameters daoParameters) throws PfModelException { for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {// Not need } @Override public <T extends PfConcept> ControlLoopvoid checkControlLoopcreate(final = controlLoopProvider.getControlLoop(controlLoop.getKey().asIdentifier()); T obj) { if (checkControlLoopobj !== null) { return; throw new PfModelException(Response.Status.BAD_REQUEST, } entityManager.merge(obj); } @Override public controlLoop.getKey().asIdentifier() + " already defined");<T extends PfConcept> void delete(final T obj) { entityManager.remove(entityManager.contains(obj) ? obj } : entityManager.merge(obj)); } BeanValidationResult validationResult = validateControlLoops(controlLoops); |
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 PolicyModelsProviderImpl implements PolicyModelsProvider { @PersistenceContext private EntityManager entityManager; @Override ifpublic ToscaServiceTemplate (!validationResult.isValid()createServiceTemplate(@NonNull ToscaServiceTemplate serviceTemplate) { LOGGER.debug("->createServiceTemplate: serviceTemplate={}", serviceTemplate); throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult()); try { } ToscaServiceTemplate controlLoopProvider.createControlLoops(controlLoops.getControlLoopList()); createdServiceTemplate = InstantiationResponse response = new InstantiationResponse(); new response.setAffectedControlLoopsSimpleToscaProvider(controlLoops).getControlLoopList().stream()appendToServiceTemplate(new DefaultPfDao(entityManager), .map(cl -> cl.getKey().asIdentifier( new JpaToscaServiceTemplate(serviceTemplate)).collecttoAuthorative(Collectors.toList())); return response; LOGGER.debug("<-createServiceTemplate: createdServiceTemplate={} |
...
", createdServiceTemplate);
return createdServiceTemplate;
} catch (Exception pfme) {
throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, pfme.getMessage(), pfme);
}
}
|
Spring Service
Example how to convert ControlLoopInstantiationProvider class in Spring style using "@Service" and "@Transactional"
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
@Service
public class ControlLoopInstantiationProvider {
private final ControlLoopProvider controlLoopProvider;
private final CommissioningProvider commissioningProvider;
/**
* Create a instantiation provider.
*
* @param databaseProviderParameters the parameters for database access
*/
public ControlLoopInstantiationProvider(ControlLoopProvider controlLoopProvider,
CommissioningProvider commissioningProvider) {
this.controlLoopProvider = controlLoopProvider;
this.commissioningProvider = commissioningProvider;
}
@Transactional
public InstantiationResponse createControlLoops(ControlLoops controlLoops) throws PfModelException {
for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {
ControlLoop checkControlLoop = controlLoopProvider.getControlLoop(controlLoop.getKey().asIdentifier());
if (checkControlLoop != null) {
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
...