JPA #Convert annotation on a method - java

The JPA #Convert annotation says it's applicable to a method (as well as field and type).
What is an example of a situation where it is useful?

#Convert allows us to map JDBC types to Java classes.
Let's consider the code block below:-
public class UserName implements Serializable {
private String name;
private String surname;
// getters and setters
}
#Entity(name = "UserTable")
public class User {
private UserName userName;
//...
}
Now we need to create a converter that transforms the PersonName attribute to a database column and vice-versa.
Now we can annotate our converter class with #Converter and implement the AttributeConverter interface. Parametrize the interface with the types of the class and the database column, in that order:
#Converter
public class UserNameConverter implements
AttributeConverter<UserName, String> {
private static final String SEPARATOR = ", ";
#Override
public String convertToDatabaseColumn(UserName userName) {
if (userName == null) {
return null;
}
....
}
}
In order to use the converter, we need to add the #Convert annotation to the attribute and specify the converter class we want to use:
#Entity(name = "PersonTable")
public class User {
#Convert(converter = UserNameConverter.class)
private UserName userName;
// ...
}
For more details you can refer below:- jpa-convert

Related

Custom Return Type on Custom CrudRepository Method

I am trying to write an interface that extends CrudRepository that will return a list of a particular field. When I use that method, I get ConverterNotFoundException. I have two questions:
Is there a specific Spring Boot query if I want a list containing a specific field?
Am I implementing the converter correctly? I am not sure how to call WebConfig.
// EmployeeRepository.java
#Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
List<String> findByEmployeeId(String employeeId); // ConverterNotFoundException. Expecting list of employee's full name
}
// EmployeeToStringConverter.java
#Component
public class EmployeeToStringConverter implements Converter<Employee, String> {
#Override
public String convert(Employee source) {
return source.getFullName();
}
}
// WebConfig.java
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new EmployeeToStringConverter());
}
}
// Employee.java
#Entity
#Data
#NoArgsConstructor
#Getter
#Table(name = "employees")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="Id")
private Long id;
#Column(name="FullName")
private String fullName;
#Column(name="NickName")
private String nickName;
public HubKey(String fullName, String nickName) {
this.fullName = fullName;
this.nickName = nickName;
}
}
// Exception when calling EmployeeRepository.findByEmployeeId()
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.jon.demo.domain.entity.Employee] to type [java.lang.String]
The converter you have registered in the WebMvcConfigurer is used for formatting data in the view(The view in MVC).
You should add converter to Spring Data related custom conversions beans, every Spring Data sub project has its own registration entry there.
Please read the Spring Data related docs.

Nested Mapping using MapStruct

class Identifier {
private long id;
private String type;
private List<Status> statuses;
}
class Customer {
private Identifier identifier;
}
class CustomerProfile {
private Customer customer;
}
class CustomerIdentifierDO {
private long id;
}
class CustomeDO {
private CustomerIdentiferDO custID;
}
class CustomerProfileDO {
private String category;
private List<Status> custStatuses;
private CustomeDO customer;
}
#Mapper
public interface CustomerProfileMapper {
CustomerProfile toCustomerProfile(CustomerProfileDO profileDO) ;
Customer toCustomer(CustomerDO customerDO);
Identifier toIdentifier(CustomerIdentifierDO identifierDO);
}
Everything works fine till this. Now I want to map custStatuses, category of CustomerProfileDO class to statuses and type of Identifier class. I've no idea how to supply CustomerProfileDO object to toIdentifier mapping method, so that I can include the mapping there itself. I tried following
#Mappings({
#Mapping(target = "customer.identifier.type", source = "category")
})
CustomerProfile toCustomerProfile(CustomerProfileDO profileDO) ;
But this nested mapping is overriding all the mapping config of below method. That should not happen.
toIdentifer(CustomerIdentifierDO identifierDO)
Is there any way to achieve this?
Currently MapStruct can pass source parameters to single methods. In order to achieve what you are looking for (without using nested target types you would need to use something like #AfterMapping. It can look like:
#Mapper
public interface CustomerProfileMapper {
CustomerProfile toCustomerProfile(CustomerProfileDO profileDO) ;
Customer toCustomer(CustomerDO customerDO);
Identifier toIdentifier(CustomerIdentifierDO identifierDO);
#AfterMapping
default void afterMapping(#MappingTarget CustomerProfile profile, CustomerProfieDO profileDO) {
Identifier identifier = profile.getCustomer().getIdentifier();
identifier.setStatus(profileDO.setStatus());
identifier.setType(profileDO.setCategory());
}
}

How to get subclasses attributes using SingleTableInheritance with SpringMVC

i am working on a web application using Spring, Hibernate and SpringMVC,
i am facing a problem with retreiving values from a subclass table using SingleTable inheritance strategy, here are my entities
Client.java (Super class)
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "typeClient", discriminatorType = DiscriminatorType.STRING)
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idClient;
private String matricule;
private String statut;
private String secteurDactivite;
private String nomClient;
private String emailClient;
private String numTelephone;
private String adresse;
//constructor
//getter & setters
}
Societe.java (subClass1)
#Entity
#DiscriminatorValue("Societe")
public class Societe extends Client implements Serializable{
private String nomResponsable;
private String emailResponsable;
private String telResponsable;
private String nomSuperieur;
private String emailSuperieur;
private String telSuperieur;
private String commentaire;
//constructeur sans parametre
public Societe() {
}
}
Particulier.java (subclass2)
#Entity
#DiscriminatorValue("Particulier")
public class Particulier extends Client implements Serializable {
private String cin;
//constructeur sans parametres
public Particulier() {
}
}
in my implementation i am using this methode to get a particular client with his ID
ClientDaoImpl.java
public class ClientDaoImpl implements ClientDao {
#PersistenceContext
private EntityManager em;
#Override
public Client getClientByID(int id_client) {
return em.find(Client.class, id_client);
}
When i ran this code i only selected the attributes of the superClass Client.
what i am trying to do is to get a client with its subclass whether it's a Societe or Particulier based on its type or clientID.
Please Help
As you don't know the type of client before querying and only it's ID, you will need to inspect the type and cast after you retrieve the record;
Client client1 = clientDao.getClientById(clientID);
if (client1 instanceof Societe) {
((Societe) client1).getCommentaire();
}
Depending on your use case, it may be useful to map the result of the client query to a ClientDescriptor object which contains all the fields for all client types and returns either nulls or blanks. This means you don't have to keep checking for client type everywhere;
public class ClientDTO {
//client fields
private String nomResponsable = "";
....
//subclass 1 fields.... initialize to empty
//subclass 2 fields .... initialize to empty
public ClientDTO (Client client) {
// set fields for client entity
}
public ClientDTO (Societe societe) {
this (societe);
// set societe fields.
}
// other constructors.
}
You can modify your getClientByID method to accept an additional argument which will say what type of entity your want to retrieve and get back:
public class ClientDaoImpl implements ClientDao {
#PersistenceContext
private EntityManager em;
public <T extends Client> T getByID(int id_client, Class<T> klass) {
return em.find(klass, id_client);
}
}
And you can use this dao in the following manner:
Societe societe = clientDao.getByID(42, Societe.class);
Particulier particulier = clientDao.getByID(43, Particulier.class);

Spring class RequestBody and MongoRepository

I have class:
class TestClass {
#Id
private ObjectId id;
private ObjectId parentId;
private String name;
private String describe;
private String privateData;
public TestClass(ObjectId parentId, String name, String describe, String privateData) {
this.parrentId = parrentId;
this.name = name;
this.describe = describe;
this.privateDate = privateData;
}
// get/set methods...
}
Can I use this class in MongoRepository and #RequestBody? Is it safe? parrentId and privateData is private properties and RequestBody does not have to fill them.
mongorepository:
public interface TestClassRepository extends MongoRepository<TestClass, String> {
public TestClass findById(ObjectId id);
}
post method:
#RequestMapping(value="/testclass", method=RequestMethod.POST)
public void create(#RequestBody TestClass testClass) {
testClass.setParentId(...);
repo.insert(testClass);
}
For example:
{"name": "test", "describe": "test", "id": "54d5261a8314fe3c650d5b1d", "parentId": "54d5261a8314fe3c650d5b1d", "privateData": "WrongPrivateData"}
How can I do that it was impossible to set properties id, parentId, privateDate?
Or need I create new class for RequestBody? I don't want duplicate code.
It should be better and safe to use separate models for DAO and VO layers(view). If your models currently looks the same, it doesn't mean that they will stay the same in future. You can use the Dozer Mapping framework for mappings between your models. It's easy,fast and safe.
If you need to skip some field from mongotemplate mapping use #Transient annotation.
P.S. You don't need findById method, because mongotemplate already have find method which uses key as param. TestClass should have an empty constructor.

How to only return certain properties of the class as JSON

I am using drop wizard which uses Jackson for returning classes as JSON.
How can I return only certain properties in a class as JSON rather than returning all the properties.
For example the User POJO
public class User {
private int id;
private String username;
private String password
//getter setters
}
and the signin path:
#GET
#Path("/signin")
public User signin(#Auth User user) {
return user;
}
returns {"password":null,"id":0,"username":"foobar"} How can I only return {"username":"foobar"}
You can annotate the field or getter/setter with
#JsonIgnore
Or annotate the class with
#JsonIgnoreProperties(value = {"password", "id"})

Categories

Resources