I have a components whose members are populated from the YAML file. I have a inner component to this component who also have some members populated from the YAML.
But when I start the app, the members of the inner component are not populated though the instance is created. The main components members are loaded up fine.
Need to know why the members of the Department components are not loaded from the YAML.
update
I can see the that inner class is loaded with the members but that is done later during the application load but the Company component is used for loading some other components before that. So I need the inner class/component to be initialized immediately after the Company component is initialized.
This is how my component is defined
#Component
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_NULL)
#ConfigurationProperties(prefix = "company")
public class Company {
#JsonProperty("connectTimeout")
private Integer connectTimeout;
#JsonProperty("socketTimeout")
private Integer socketTimeout;
#JsonProperty("clientTimeout")
private Integer clientTimeout;
#JsonProperty("dept1")
#Autowired
private Department library;
#JsonProperty("dept2")
#Autowired
private Department admin;
#JsonProperty("dept3")
#Autowired
private Department transport;
#JsonProperty("dept4")
#Autowired
private Department finance;
//Getters and Setters
#Component
#SuppressWarnings({"PublicInnerClass", "WeakerAccess"})
public static class Department {
#JsonProperty("connectTimeout")
private Integer connectTimeout;
#JsonProperty("socketTimeout")
private Integer socketTimeout;
#JsonProperty("clientTimeout")
private Integer clientTimeout;
//Getters and Setters
}
}
The below is my YAML file
company:
connectTimeout: 1000
socketTimeout: 20000
clientTimeout: 150
dept1:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
dept2:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
dept3:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
dept4:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
Assume we have the following property holder dto's Company & Department;
public class Company {
private Integer connectTimeout;
private Integer socketTimeout;
private Integer clientTimeout;
private Department library;
private Department admin;
private Department transport;
private Department finance;
public void setDept1(Department dept1) {
this.library = dept1;
}
public void setDept2(Department dept2) {
this.admin = dept2;
}
public void setDept3(Department dept3) {
this.transport = dept3;
}
public void setDept4(Department dept4) {
this.finance = dept4;
}
// other getter/setters
public static class Department {
private Integer connectTimeout;
private Integer socketTimeout;
private Integer clientTimeout;
// getter/setters
}
}
& just define a config property containing bean with;
#Configuration
public class MyConfiguration {
#Bean
#ConfigurationProperties(prefix = "company")
public Company company() {
return new Company();
}
}
Using #JsonProperty wouldn't work with such properties, since that annotation only works with de/serialization through Jackson. #ConfigurationProperties focuses on basic getter/setters, just adding setters with those names is sufficient, e.g. setDept1, setDept2 etc.
Read more on externalized configuration on Spring Boot, here
Related
I want to run different instances of my application in which the entity is different.
For example, one instance of application will have all the 3 attributes of customer table and another instance will have only 2 attributes of the customer table ( say firstName and lastName only).
Is there any way to configure it through the application.properties files / spring-boot provides any annotation that can handle this ?
input dto for postman
{
"id":1,
"firstName":"Tom",
"lastName":"Holland",
"address":{
"streetName":"London Streets",
"houseNumber":"123"
}
}
In case that property is set as off, then the table consists of id,firstName , lastName and Address.streetName,address.houseNumber .
In case that property is set as on, then the table consists of only id,firstName and lastName .
Controller Class
#RestController
public class CustomerController {
#Autowired
private CustomerService service;
#PostMapping("/customer")
public String addCustomer(#RequestBody Customer c)
{
service.addCustomer(c);
return "Customer added successfully " ;
}
}
Customer entity
#Entity
#Data
public class Customer {
#Id
private Long id;
private String firstName;
private String lastName;
// any spring property to configure this from app.properties
#Embedded
private Address address;
}
Address table
#Data
public class Address {
private String streetName;
private String houseNumber;
}
service layer
#Service
public class CustomerService {
#Autowired
private CustomerRepository customerRepo;
public void addCustomer(Customer customer)
{
customerRepo.save(customer);
}
}
app.properties
spring.datasource.url = jdbc:postgresql://localhost:5432/customerDirectory
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.hibernate.ddl-auto=create-drop
We are building an application that needs to store differences made on existing opbjects.
Afterwards we will store these changes to the database. However for some reason the #diffIgnore option is not working on our User.class.
Every object extends our baseEntityCMS class, which has a property User. This is meant to store our update user information after the compare by JaVers is done. For some reason the user object is still compared even after setting #diffIgnore on property level and on class level.
Here is the code:
BaseEntityCMS.java
public class BaseEntityCMS extends BaseEntity {
private Boolean active;
private LocalDateTime inactiveDateTime;
private LocalDate creationDate;// in original application
private LocalDate importDate;
private LocalDate startDate;
private LocalDate endDate;
#Embedded
#DiffIgnore
private User modifierUser;
...
}
CodeList.java
public class CodeList extends BaseEntityCMS {
private String companyCode;
private Application sourceApplication;
private String name;
private String format;
private int length;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "codeList")
private List<Description> descriptionList;
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "codeListList")
private List<Keyword> keywords;
private String domainOwner;
#OneToMany(cascade = CascadeType.REFRESH)
private List<Attribute> attributeList;
...
}
User.java
#Embeddable
#AllArgsConstructor
#Builder
#DiffIgnore
public class User {
private String userName;
}
TestCodeList.java
#Test
public void compareCodeListTest() {
Javers javers = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.LEVENSHTEIN_DISTANCE)
.registerValueObject(BaseEntity.class).registerValue(Code.class).registerValue(Attribute.class)
.registerValue(AttributeValue.class).registerValue(MapCodeAttribute.class)
.registerValueObject(User.class).build();
CodeList codeListNew = createCodeListTest("ServiceTest");
CodeList codeListNew2 = createCodeListTest("ServiceTest");
Diff diff = javers.compare(codeListNew2, codeListNew);
assertNull(diff.getChanges());
}
So in the test class we create 2 CodeLists (codeListNew and codeListNew2) with a standard method.
Inside this method everything is created the same except we create a new User every time.
Because all properties of codeList (attribute, attributeValue, ...) extend the BaseEntityCMS class they all have a User property.
This is the output we get:
changes on xxx.CodeList/ :
- 'attributeList' collection changes :
1. 'Attribute(name=Address, format=freeformat, length=10, numberOfDecimals=2, description=description attribute, optional=true)' changed to 'Attribute(name=Address, format=freeformat, length=10, numberOfDecimals=2, description=description attribute, optional=true)'
0. 'Attribute(name=Number of employees, format=freeformat, length=10, numberOfDecimals=2, description=description attribute, optional=true)' changed to 'Attribute(name=Number of employees, format=freeformat, length=10, numberOfDecimals=2, description=description attribute, optional=true)'
- 'mapCodeAttributeMap' map changes :
'Code(super=BaseEntityCMS(super=BaseEntity(id=null), active=true, inactiveDateTime=null, creationDate=2020-03-23, importDate=2020-03-23, startDate=2020-03-23, endDate=2025-12-31, modifierUser=codems.agza.datalayer.model.User#57f83dc7), code=code1)' -> 'MapCodeAttribute(super=BaseEntity(id=null))' added
'Code(super=BaseEntityCMS(super=BaseEntity(id=null), active=true, inactiveDateTime=null, creationDate=2020-03-23, importDate=2020-03-23, startDate=2020-03-23, endDate=2025-12-31, modifierUser=codems.agza.datalayer.model.User#75937998), code=code1)' -> 'MapCodeAttribute(super=BaseEntity(id=null))' removed
We actually expect no difference since everything is the same but the User. This user is marked with #DiffIngore? How come we still have differences?
Forgot: we use
<dependency>
<groupId>org.javers</groupId>
<artifactId>javers-core</artifactId>
<version>5.8.11</version>
</dependency>
Animal.java
#Data
#Entity
public class Animal implements MyEntityInterface {
public enum Sex {MALE, FEMALE}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private Sex sex;
private boolean castrated;
#OneToMany
private List<Symptom> symptoms;
}
AnimalDTO.java
#Getter
#Setter
public class AnimalDTO implements Serializable {
private long id;
private String name;
private Animal.Sex sex;
private boolean castrated;
private List<Long> symptoms;
}
I wish for a list of Symptoms to be automatically mapped to a list of ID's. This could be achieved in many ways, such as creating a TypeMap, creating a Converter or even just by creating a method in AnimalDTO.java:
public void setSymptoms(List<Symptom> symptoms) {
if (symptoms != null)
this.symptoms = symptoms.stream().map(s -> s.getId()).collect(Collectors.toList());
}
But now imagine it's not only Symptoms, but 50 other fields too. That's a lot of code for the same functionality. And then, it's not only Animal to AnimalDTO, but another 30 different classes with their respective DTOs too.
Also, that still leaves the way back open. From ID to entity. This can (in theory) be achieved easily with the following pseudocode:
List<EntityMemberField.class> list;
for (var entityid : listOfEntityIDsOfDto) {
Object persistedObject = entityManager.find(EntityMemberField.class, entityid);
list.add(persistedObject);
}
...
ModelMapperDestination.setField(list);
This is the same for absolutely every Entity/DTO and should automatically happen for every Entity relationship where the Entity implements MyEntityInterface.
An idea how I could achieve that would be overriding MappingEngineImpl.java from ModelMapper which I register as a Spring Service and inject the EntityManager into, but how could I get ModelMapper to use mine? Or is there maybe an easier way?
The goal is to have a fairly automated conversion from Spring Entities to their corresponding DTO by... just calling modelMapper.map(entity, EntityDTO.class);
I am trying to establish the aggregation relationship between two Java classes through JPA annotations in order to persist them into a database.
public class Ticket
{
private String ticketNo;
private Date releasedDate;
private boolean printed;
}
public class Discount
{
private String percentage;
private Date releasedDate;
private boolean printed;
}
Such as mentioned here, the aggregation relationship is unidirectional and thus, only it is necessary to map one side. From the solution given by this page, I think the solution will be:
public class Discount
{
private String percentage;
private Date releasedDate;
private boolean printed;
#ManyToOne(name="TICKET_ID")
private Ticket ticket;
}
However, in some examples of aggregation, the many side class appears inside the one side class. Thus, I am considering this too:
public class Ticket
{
private String ticketNo;
private Date releasedDate;
private boolean printed;
#OneToMany(mappedBy="ticket")
private List<Discount> discounts = new ArrayList<Discount>();
}
Which option is the proper one?
This how you map a unidirectional many-to-one relationship:
#Entity
public class Ticket {
#Id
#GeneratedValue
private Long id;
private String ticketNo;
private Date releasedDate;
private boolean printed;
// getters and setters
}
#Entity
public class Discount {
#Id
#GeneratedValue
private Long id;
private String percentage;
private Date releasedDate;
private boolean printed;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "TICKET_ID") // you can rename the join column
private Ticket ticket;
// getters and setters
}
Note:
JoinColumn (foreign key in the database terminology) must be on the many side of the relationship (this is the Discount in your case).
The #Id annotations are also mandatory. In this case, the ID will be generated by the persistence provider automatically. If you are using database sequence or table or some other strategy you can redefine it.
That looks right to me. A discount has a ticket. You could also include the discounts accessible from the tickets like ticket.getDiscounts() if you need to access them in a query such as SELECT t FROM Ticket t WHERE t.discounts.percentage >= :discountPercentage.
#Entity
public class Ticket {
#Id
private String ticketNo;
private Date releasedDate;
private boolean printed;
#OneToMany(mappedBy = "ticket", fetch = FetchType.LAZY)
private List<Discounts> discounts;
}
#Entity
public class Discount {
private String percentage;
private Date releasedDate;
private boolean printed;
#ManytoOne(name="TICKET_ID")
private Ticket ticket;
}
However, I wouldn't recommend using #OneToMany as this can create problems serializing too much data to JSON if you are returning this as JSON results or just lazily loading too much data by accident. You should always be able to work with just #ManyToOne as an example if you did not put the #OneToMany association query can be SELECT t FROM Discount d INNER JOIN d.ticket t WHERE d.percentage >= :discountPercentage
I have 3 classes as shown below.
#Entity
public class Family (
#Id
private String familyName;
private int size;
#OneToMany
protected VehiclesList getVehiclesList()
// getters and setters
)
public class VehiclesList (
private List<Vehicle> vehicles;
#Transient
private int totalInsuranceCost
// getters and setters
}
#Entity
public class Vehicle (
#Id
private String plateNumber;
private String model;
private String color;
// getters and setters
)
I want to create two tables. First one is "Family" with columns as "size", etc. This is easy enough. Second, I want to create a "Vehicle" table with reference to Family. This table should have the following columns:
plateNumber
FamilyName
model
color
I would like the Family class to reference the VehiclesList class, so that I can access information from the VehiclesList class. None of the data in the VehiclesList class will be persisted to the database - only the vehicles. How can I do this?
VehicleList is not an entity so you cannot use OneToMany on it.
totalInsuranceCost should not be a property like this but more something for a service method like
VehicleService.calculateTotalInsuranceCostForFamily (String familyName).
Your family entity then becomes:
#Entity
public class Family (
#Id
private String familyName;
private int size;
#OneToMany(mappedBy = "family")
private List<Vehicle> vehicles;
// getters and setters
)