Mixed Entity and Business classes - Refactor help needed - refactoring

I have a project where Entity Classes and Business classes are mixed up. The entity beans are part of the business and all is used through the whole project.
How can I best refactor those classes to separate those layers. I also want to keep the changes to the implementers as minimal as possible. Preferable no changes, otherwise hundreds of references need to be updated.
How should I rename the classes and work through this?
Example of mixed code:
// Mixed business-entity class
public final class Language {
private final Long id;
private final String code;
private final String description;
//Constructor
public Language() {
}
//getters and setters
public String getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
...
//Business is a part of this class
public static Language findByUser(User user) {
Language language;
...implementation to find user language...
return language;
}
....
}
// Implementing class
public class Messenger {
public Messenger() {
}
public static void sendEmail() {
...
Language emailLanguage = Language.findByUser(user):
...
}
}
I want to separte those layers in:
// Entity Class
public final class Language {
private final Long id;
private final String code;
private final String description;
//Constructor
public Language() {
}
//getters and setters
public String getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
...
}
// Business Class
public final class LanguageImpl {
public LanguageImpl() {
}
public static Language findByUser(User user) {
Language language;
...implementation to find user language...
return language;
}
....
}
Provide minimal changes to implementation classes, preferable no changes. Otherwise a lot of work will come because of the references all over the code-base.
// Implementing class
public class Messenger {
public Messenger() {
}
public static void sendEmail() {
...
Language emailLanguage = Language.findByUser(user);
...
}
}
How do I work through this refactoring?
How should I rename my classes?
Any thoughts would be very helpful! Thanks!

This is my solution. Please review and accept this if it looks good. Thanks!
The mixed business-entity class is re-used as a wrapper class. This makes it possible to re-use this in all implementing classes where no changes are needed.
public final class Language Extends LanguageImpl{
private final LanguageEntity languageEntity;
//Constructor
public Language(LanguageEntity le) {
languageEntity = le;
}
//Wrapper method
public static Language findByUser(User user) {
LanguageEntity le = findEntityByUser(user);
Language language = new Language(le);
return language;
}
....
}
A new Entity class is created (LanguageEntity) in a new package. This avoids package and naming conflicts with the original mixed class (Language). All entity fields and methods from the mixed class are moved here.
package com.test.entity;
public final class LanguageEntity {
private final Long id;
private final String code;
private final String description;
//Constructor
public LanguageEntity() { }
//getters and setters
public String getId() { return this.id; }
public void setId(Long id) { this.id = id; }
...
}
A new business class is created (LanguageImpl) in a new package. All business methods are moved here. The original mixed class will extend this new business class.
package com.test.impl
public final class LanguageImpl {
//Constructor
public LanguageImpl() { }
//Business is a part of this class
public static LanguageEntity findEntityByUser(User user) {
LanguageEntity language;
...implementation to find user language...
return language;
}
....
}
This is an implementing class that does not need changes. Hundreds of implementation locations remain unchanged, which saves a lot of work. Hurray!
public class Messenger {
public Messenger() { }
public static void sendEmail() {
...
Language emailLanguage = Language.findByUser(user):
...
}
}
And for future development, the new combination LanguageEntity and LanguageImpl will be used. The original Language will be deprecated.
Please leave comments on this solution. Other solutions are more than welcome!

Related

Spring Boot Rest

i am practicing with spring boot for work with restful applications
I have set a #RestController and #Entity like this
#RestController
#RequestMapping(value = "/api")
public class RestControllerCar {
#Autowired
private CarRepository carRepository;
#RequestMapping(value = "/cars")
public Iterable<Car> getCars() {
return carRepository.findAll();
}
}
and
#Entity
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String brand, model, color, registerNumber;
private Integer year, price;
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#ManyToMany(mappedBy = "cars")
private Set<Owner> owners;
public Car() {
}
public Car(String brand, String model, String color, String registerNumber, Integer year, Integer price) {
super();
this.brand = brand;
this.model = model;
this.color = color;
this.registerNumber = registerNumber;
this.year = year;
this.price = price;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getRegisterNumber() {
return registerNumber;
}
public void setRegisterNumber(String registerNumber) {
this.registerNumber = registerNumber;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Set<Owner> getOwner() {
return owners;
}
public void setOwner(Set<Owner> owners) {
this.owners = owners;
}
when i use postman to http://localhost:8080/cardatabase/api/cars i get a list of Cars
but even if i go to http://localhost:8081/cardatabase/cars, with _embedded on the top
it`s normal?
Thanks!!!!
Is your repository annotated #RestRepository? The _embedded make me think to the kind of output given by a #RestRepository for an array.
#RestRepository auto create all endpoint. As #M.Deinum pointed out, with the data rest starter, if ou remove it , you only have your controller, and not the one generated by #RestRepository.
Two main choices here:
You dont annotate the Repository. Just an interface which implement JpaRepository<YourEntity, TypeOfYourID> and use your controllers
You use only the auto created controllers by #RestRepository.
Or, you can install swagger2 on your project, so, accessing the docs on your browser, you will see all available endpoints, and it may be more clear for you.
With swagger you will also see what is the return type of the endpoint, the parameters etc..
Swagger is really easy to install in a project and to use. (dependencies, one annotation and it's good.. for basic usage).

Use of ConfigurationProperties with inner class results in "properties were left unbound" exception

I have a #ConfigurationProperties-annotated #Configuration-class called LibraryConfig. It uses an inner class as a type definition for a property/configuration struture. When the class is an inner class instead of a standalone class I get "Elements [...] were left unbound" errors/exceptions. Why is this so and how can I fix it?
application.yml
initdata:
library:
name: awesome library
books:
- title: Book1
author: Author Abc
- title: Book2
author: Author Xyz
LibraryConfiguration.java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
#Configuration
#ConfigurationProperties(prefix = "initdata.library")
public class LibraryConfiguration {
private String name;
private List<Book> books;
// getters left out for simplicity of example
public void setName(String name) {
this.name = name;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public LibraryConfiguration() {
}
public static class Book {
private String title;
private String author;
public Book() {
}
// getters left out for simplicity of example
public void setTitle(String title) {
this.title = title;
}
public void setAuthor(String author) {
this.author = author;
}
}
}
You either create a standalone class or you must declare inner classes as static class! Otherwise Spring doesn't see it just as a "type definition" but tries to create an instance of it - which of course doesn't work with the given yml-configuration. Change the code to the following
// ...
public LibraryConfiguration() {
}
// This class MUST be static, otherwise Spring will tell "Elements [...] were left unbound".
// If it's static, it's just a "type definition";
// otherwise spring tries to create an insance
public static class Book {
private String title;
private
// ...

Custom Source presence checking method name in MapStruct

is it posible to generate a custom "presence checking" method name, being a method of the property itself rather the owning object?
I know I can use hasProperty() methods to check for presence of a value...
https://mapstruct.org/documentation/stable/reference/html/#source-presence-check
but with Optional or JsonNullable (from OpenApi nonullable) that checking method is on the property itself, not on the owning object... :-(
I can map JsonNullable or Optional easyly 'using' or extending a simple custom Mapper
#Mapper
public class JsonNullableMapper {
public <T> T fromJsonNullable(final JsonNullable<T> jsonNullable) {
return jsonNullable.orElse(null);
}
public <T> JsonNullable<T> asJsonNullable(final T nullable) {
return nullable != null ? JsonNullable.of(nullable) : JsonNullable.undefined();
}
}
what I would like to achieve is something like this as "presence check":
if(source.getProperty().isPresent()) {
target.set(customMapper.map(source.getProperty()));
}
Any one found a solution for this?
Thanks and regards
I have managed to implement custom lombok extension which generates "presence checknig" methods.
Here is an example project. In short I added #PresenceChecker annotation and implemented Lombok Javac Annotation handler.
It's possible to use it together with other Lombok annotations:
#Getter
#Setter
public class User {
private String name;
}
#Getter
#Setter
#PresenceChecker
public class UserUpdateDto {
private String name;
}
//MapStruct Mapper interface declaration
#Mapper
public interface UserMapper {
void updateUser(UserUpdateDto dto, #MappingTarget User user);
}
Generated code:
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
//MapStruct Mapper implementation
public class UserMapperImpl implements UserMapper {
#Override
public void updateUser(UserUpdateDto dto, User user) {
if ( dto == null ) {
return;
}
if ( dto.hasName() ) {
user.setName( dto.getName() );
}
}
}
The answer is unfortunately a straight no.
It is not possible in the current version of MapStruct (1.3.1final) and its not on the shortlist for 1.4.0. You could open up an issue on the git repo of MapStruct as feature request.

Sonarqube not finding possible null pointer exception

In the below code, Why sonarqube is not finding possible null pointer exception in "updateData" method?
public class PropertyObject extends LinkedHashMap<String, Object> {
/**
* Unique serialization id.
*/
private static final long serialVersionUID = 4789053897514939L;
}
public class BaseObject extends PropertyObject {
#JsonProperty("_id")
public String getId() {
return String.valueOf(this.get("_id"));
}
#JsonProperty("_id")
public void setId(Object id) {
this.put("_id", String.valueOf(id));
}
public String getName() {
return (String) this.get("name");
}
public void setName(String name) {
this.put("name", name);
}
}
private void updateData(BaseObject baseObject) {
List<Map<String, String>> link = (List<Map<String, String>>) baseObject.get("ratioMap");
for (Map<String, String> linkmap : link) {
}
}
}
I can see potential null pointer exception in updateData method in line number 2.
Is there any way by which I can make sonarqube to find these issues by itself?
First of all Sonar is a static code analysis tool. It depends on simple declarations to look for possible NPEs. Second I assume that you have an active rule for detecting possible NullPointer dereferences.
Last but not least I think that it would not detect NPEs in private methods which is not called...

Spring MVC Generics Object Binding/Type Conversion

I'm looking for a way to wrap my models or DTOs with a generic class to add a 'selected' Boolean property and be able to bind the object in my POST method controller.
Like this:
public class RowForm<T> implements Serializable {
private static final long serialVersionUID = 1L;
private T model;
private Boolean selected=false;
public RowForm() {
super();
}
public RowForm(T model) {
super();
this.model = model;
}
public T getModel() {
return model;
}
public void setModel(T model) {
this.model = model;
}
public Boolean getSelected() {
return selected;
}
public void setSelected(Boolean selected) {
this.selected = selected;
}
}
public class ProductsForm implements Serializable{
private static final long serialVersionUID = 1L;
private RowForm<Product> row;
//...other stuff and getters/setters
}
and use it like this:
#PostMapping ("/postProduct")
public String POSTproduct(Model model, #ModelAttribute ProductsForm pf)
{
....
}
But I'm stuck with the conversion...
When I call pf.getRow() it returns a plain Object, not a RowForm<Product>.
How can I implement a ConversionService/PropertyEditor to bind my posted data to my extended generic object?
Why not use a abstract base class with the selected property, and extend it for your DTOs?

Resources