-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This wiki provides an overview of the process of converting a Spring Application to a Quarkus Application.
Java 11 or higher Maven 3.6.3 or higher
In order to convert the Spring Application to a Quarkus application, we will need the necessary Quarkus dependencies:
mvn io.quarkus:quarkus-maven-plugin:1.4.2.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=survey-quarkus-spring-service \
-DclassName="com.redhat.appdevpractice.samples.survey.controller" \
-Dextensions="spring-web,spring-di,spring-data-jpa,resteasy-jsonb,jdbc-postgres,hibernate-orm,rest-assured,hibernate-orm,quarkus-agroal" \
-Dpath="/hello"
Create a local copy of the AppDev Spring Rest Survey Group repository
git clone https://github.com/redhat-appdev-practice/spring-rest-surveygroups.git
Replace the contents of the Spring Application pom.xml with the contents of the Quarkus Application pom.xml.
Prerequisites: Add the following imports to SurveyController.java
import javax.enterprise.context.ApplicationScoped;
import javax.transaction.Transactional;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
Add the @ApplicationScoped and @RegisteredClient annotations to the top of the SurveyController class:
@CrossOrigin
@RestController
@ApplicationScoped
@RegisterRestClient
public class SurveyController { ... }
Quarkus does not support the ServletUriComponentsBuilder. Instead we will use the Javax UriInfo and Context. Declare the following:
@Context
private UriInfo info;
First, add the @Transactional annotation to the createSurvey method. Quarkus contains a Transactional manager that that coordinates and exposes transactions to your applications. Each extension dealing with persistence will integrate with it for you. To learn more: https://quarkus.io/guides/transaction
@PostMapping("/surveygroups")
@Transactional
public ResponseEntity<String> createSurvey(@RequestBody NewSurveyGroupResource newSurveyGroup) { .. }
Secondly, replace the URI URI location =
line to the following:
URI location = info.getAbsolutePathBuilder().path("/{surveyGroupId}").build(surveyGroup.getGuid());
Add the @Transactional annotation.
@GetMapping("/surveygroups/{surveyGroupId}")
@Transactional
public ResponseEntity<SurveyGroupResource> getSurveyGroup(@PathVariable("surveyGroupId") String surveyGroupId) { ... }
Add the @Transactional annotation.
@GetMapping("/surveygroups/{surveyGroupId}")
@Transactional
public ResponseEntity<SurveyGroupResource> getSurveyGroups() { ... }
Add the @Transactional annotation.
@PutMapping("/surveygroups/{surveyGroupId}")
@Transactional
public ResponseEntity<SurveyGroupResource> saveSurveyGroup(@PathVariable String surveyGroupId,
@RequestBody SurveyGroupResource surveyGroupResource) { ... }
The current Spring Service uses a JPA Repository. Quarkus offers Panache, a Hibernate ORM with a JPA implementation. It makes complex mappings possible, but it does not make simple and common mappings trivial. To learn more: https://quarkus.io/guides/hibernate-orm-panache
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class SurveyGroupRepository implements PanacheRepository<SurveyGroup> {
public SurveyGroup findByGuid(String surveyGroupGuid) {
return find("guid", surveyGroupGuid).singleResult();
}
public void deleteByGuid(String string) {
delete(findByGuid(string));
}
}
Not only do we need to extend the PanacheEntity, but we need to also format the dates with @JsonFormat so that they can be deserialized by the database.
Add the following import statements:
import com.fasterxml.jackson.annotation.JsonFormat;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
Extend the PanacheEntity on the EmployeeAssignment class:
@Entity
public class EmployeeAssignment extends PanacheEntity { ... }
Add the following @JSONFormat annotations to the LocalDate fields (allow for deserialization):
@JsonFormat(pattern = "YYYY-MM-dd")
private LocalDate startProjectDate;
@JsonFormat(pattern = "YYYY-MM-dd")
private LocalDate endProjectDate;
We must also configure the fetch type to be LAZY for surveyGroup:
@ManyToOne(fetch = FetchType.LAZY)
@JoinTable(name = "surveygroup_employee",
joinColumns = { @JoinColumn(name = "employee_id") },
inverseJoinColumns = { @JoinColumn(name = "id") })
private SurveyGroup surveyGroup;
Add the following import:
import io.quarkus.hibernate.orm.panache.PanacheEntity;
Extend the PanacheEntity:
@Entity
public class Skill extends PanacheEntity { ... }
Add the following import:
import io.quarkus.hibernate.orm.panache.PanacheEntity;
Extend the PanacheEntity:
@Entity
public class SurveyGroup extends PanacheEntity { ... }
In employeeAssignments @OneToMany annotion, assign the fetch type to LAZY
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "surveygroup_employee_assignments",
joinColumns = { @JoinColumn(name = "id") },
inverseJoinColumns = { @JoinColumn(name = "employee_assignment_id") })
private List<EmployeeAssignment> employeeAssignments;
Remove the following import:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Add the following imports:
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.NoResultException;
import javax.transaction.Transactional;
Replace the @Service annotation on the SurveyServiceImpl with @ApplicationScoped:
@ApplicationScoped
public class SurveyServiceImpl implements SurveyService { ... }
Add an @Inject annotation to the surveyGroupRespository declaration:
@Inject
private SurveyGroupRepository surveyGroupRepository;
Edit createSurveyGroup by:
- Adding @Transactional annotation
- changing .saveandflush (JPA method) to .persistandflush (Panache method)
- Because the .persistAndFlush method does not return anything, we return the result of .findByGuid(guid)
Replace method body with the following:
@Transactional
public SurveyGroup createSurveyGroup(SurveyGroup surveyGroup) {
String guid = UUID.randomUUID().toString();
surveyGroup.setGuid(guid);
surveyGroupRepository.persistAndFlush(surveyGroup);
return surveyGroupRepository.findByGuid(guid);
}
Edit getSurveyGroup by:
- Adding @Transactional annotation
- adding the .list() to return statement which allows Panache to return a list
@Transactional
public List<SurveyGroup> getSurveyGroups() {
return surveyGroupRepository.findAll().list();
}
Edit updateSurveyGroup by:
- Adding @Transactional annotation
@Transactional
public SurveyGroup getSurveyGroup(String surveyGroupGuid) {
SurveyGroup surveyGroup = surveyGroupRepository.findByGuid(surveyGroupGuid);
if( surveyGroup == null){
throw new ResourceNotFoundException("The survey group does not exist.");
}
return surveyGroup;
}
Edit updateSurveyGroup by:
- Adding @Transactional annotation
- change .saveAndFlush to .persistAndFlush
@Override
@Transactional
public SurveyGroup updateSurveyGroup(SurveyGroup oldSurveyGroup, SurveyGroup newSurveyGroup) {
oldSurveyGroup.updateWith(newSurveyGroup);
surveyGroupRepository.persistAndFlush(oldSurveyGroup);
return oldSurveyGroup;
}
Springboot currently uses an application-local.yml to setup its connection to the database.
Instead create an application.properties file in src/main/resources
with the following:
---
# datasource configuration
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/hibernate_db
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = hibernate
quarkus.datasource.password = hibernate
# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
To learn more : https://quarkus.io/guides/hibernate-orm
Coming Soon..