I have a Entity class something like this:
#Entity
public class Website {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String url;
public Website() {
//Constructor
//getters and setters
}
here is the DTO class:
public class WebsiteDto {
private Integer id;
private String name;
private String url;
public WebsiteVo() {
//Constructor
//getters and setters
}
I have the WebsiteMapper something like this:
#Component
public class WebsiteMapper {
public List<WebsiteDto> getWebsiteList() {
return repository.findAll().stream().map(w -> {
WebsiteDto dto = new WebsiteVo(w.getId(), w.getName(), w.getUrl());
return dto;
}).collect(Collectors.toList());
I also have Repository Interface:
public interface WebsiteRepository extends JpaRepository<Website, Integer> {
}
I want now to convert DTO to entity using my class WebsiteMapper. Because I did the conversion in this class. How I can do it?
How about using BeanUtils provided by spring org.springframework.beans.BeanUtils, something like this
public List<WebsiteDto> getWebsiteList() {
return repository.findAll().stream().map(w -> {
WebsiteDto dto = new WebsiteVo();
BeanUtils.copyProperties(w, dto); // copys all variables with same name and type
return dto;
})
.collect(Collectors.toList());
}
Hi I guess you wish to converting your entity to DTO. It's quite simple. Create static methods in your DTO class or any util class. The return type should be your DTO type.
e.g.
public class WebsiteDto {
private Integer id;
private String name;
private String url;
public static WebsiteDto export(Website website) {
// Return a new instance of your website DTO
return new WebsiteDto(
website.getId(),
website.getName(),
website.getUrl()
);
}
public static List<WebsiteDto> export(List<Website> websites) {
// Return a new instance of your website DTO list
return websites.stream().map(website -> {
return new WebsiteDto(
website.getName(),
website.getUrl()
}).collect(Collectors.toList());
}
}
NOTE You can also convert your DTO to entity using similar method.
Related
I'm developing a website for my company, and I use Spring as my backend.
There is a situation now, where I need to use one of my Utils method twice, but for different DAOs.
In order to avoid code duplication, I was wondering how can I use Java Generics in order to make this method usable for both cases. The method just count one of the fields which is common for both DAOs.
Util method :
SeverityCount calculateSeveritiesCount(List<?> events){
if(null == events){
return new SeverityCount();
}
if(events.get(1) instanceof EventDAO){
events = (List<EventDAO>)events;
}
else if (events.get(1) instanceof EventsByAreaDAO) {
events = (List<EventsByAreaDAO>)events;
}
Map<String, Long> severityCountMap = events.stream().collect(
Collectors.groupingBy(
EventDAO::getSeverity, //It should be EventDAO or EventsByAreaDAO. both has severity field.
Collectors.counting())
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
Event DAO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "events")
public class EventDAO {
#Id #Column(name = "uid")
private String uID;
private String date;
private String severity;
}
Area DAO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "events")
public class EventsByRegionDAO {
#Id #Column(name = "uid")
private String uID;
private String date;
private String type;
private String severity;
private String area;
private String server;
}
This is how I call it from the service:
SeverityCount severitiesCount = Utils.calculateSeveritiesCount(eventsList); //EventsList could be list of EventDAO or EventsByAreaDAO
You can change the method to
SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events)
where SeverityCalculable
interface SeverityCalculable {
String getSeverity(); // implemente getter in all subclasses
}
Have all your relevant classes implement this interface.
public class EventDAO implements SeverityCalculable {
// ...
#Override
public String getSeverity() {
return this.severity;
}
}
Now in your method, remove the casts and it should become something like this:
SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events) {
if(null == events){
return new SeverityCount();
}
Map<String, Long> severityCountMap = events.stream().collect(
Collectors.groupingBy(
SeverityCalculable::getSeverity,
Collectors.counting()
)
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
Since both DAO's have a severity property, they could potentially implement a common interface, say SeverityAware:
public interface SeverityAware {
public String getSeverity();
}
public class EventsByRegionDAO implements SeverityAware { .. }
public class EventDAO implements SeverityAware { .. }
Further, your method could now accept subtypes of this interface:
SeverityCount calculateSeveritiesCount(List<? extends SeverityAware> events){
if (null == events){
return new SeverityCount();
}
Map<String, Long> severityCountMap = events.stream().collect(Collectors.groupingBy(
SeverityAware::getSeverity,Collectors.counting())
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
I have MyEntity class:
#Entity
#Table("entities)
public class MyEntity {
#ID
private String name;
#Column(name="age")
private int age;
#Column(name="weight")
private int weight;
...getters and setters..
}
In #RestController there are 2 #GetMapping methods.
The first:
#GetMapping
public MyEntity get(){
...
return myEntity;
}
The second:
#GetMapping("url")
public List<MyEntity> getAll(){
...
return entities;
}
It's needed to provide:
1. #GetMapping returns entity as it's described in MyEntity class.
2. #GetMapping("url") returns entities like one of its fields is with #JsonIgnore.
UPDATE:
When I return myEntity, client will get, for example:
{
"name":"Alex",
"age":30,
"weight":70
}
I want in the same time using the same ENTITY have an opportunity depending on the URL send to client:
1.
{
"name":"Alex",
"age":30,
"weight":70
}
2.
{
"name":"Alex",
"age":30
}
You could also use JsonView Annotation which makes it a bit cleaner.
Define views
public class View {
static class Public { }
static class ExtendedPublic extends Public { }
static class Private extends ExtendedPublic { }
}
Entity
#Entity
#Table("entities)
public class MyEntity {
#ID
private String name;
#Column(name="age")
private int age;
#JsonView(View.Private.class)
#Column(name="weight")
private int weight;
...getters and setters..
}
And in your Rest Controller
#JsonView(View.Private.class)
#GetMapping
public MyEntity get(){
...
return myEntity;
}
#JsonView(View.Public.class)
#GetMapping("url")
public List<MyEntity> getAll(){
...
return entities;
}
Already explained here:
https://stackoverflow.com/a/49207551/3005093
You could create two DTO classes, convert your entity to the appropriate DTO class and return it.
public class MyEntity {
private String name;
private int age;
private int weight;
public PersonDetailedDTO toPersonDetailedDTO() {
PersonDetailedDTO person = PersonDetailedDTO();
//...
return person;
}
public PersonDTO toPersonDTO() {
PersonDTO person = PersonDTO();
//...
return person;
}
}
public class PersonDetailedDTO {
private String name;
private int age;
private int weight;
}
public class PersonDTO {
private String name;
private int age;
}
#GetMapping
public PersonDTO get() {
//...
return personService.getPerson().toPersonDTO();
}
#GetMapping("/my_url")
public PersonDetailedDTO get() {
//...
return personService.getPerson().toPersonDetailedDTO();
}
EDIT:
Instead of returning an Entity object, you could serialize it as a Map, where the map keys represent the attribute names. So you can add the values to your map based on the include parameter.
#ResponseBody
public Map<String, Object> getUser(#PathVariable("name") String name, String include) {
User user = service.loadUser(name);
// check the `include` parameter and create a map containing only the required attributes
Map<String, Object> userMap = service.convertUserToMap(user, include);
return userMap;
}
As an example, if you have a Map like this and want
All Details
userMap.put("name", user.getName());
userMap.put("age", user.getAge());
userMap.put("weight", user.getWeight());
Now if You do not want to display weight then you can put only two
parameters
userMap.put("name", user.getName());
userMap.put("age", user.getAge());
Useful Reference 1 2 3
I have the following class structure:
public abstract class Creature{
private String name;
//strategy pattern composition
private SkillInterface skill;
}
public interface SkillInterface {
void attack();
}
public class NoSkill implements SkillInterface {
#Override
public void attack() {
//statements
}
}
My goal is to persist Creature objects at one table in database. Subclasses of SkillInterface are without any fields. As they determine the behaviour, I want to convert selected SkillInterface class name to a String, as I only need to persist the classname of the current skill strategy of creature, with a String like skill.getClass().getSimpleName(). I tried to implement it with #Converter annotation, using AttributeConverter class to convert SkillInterface to String and save, but always had mapping exceptions. I want to be able to save it as String and retrieve as SkillInterface object.
But how can I implement it with Hibernate? Or do I have a design mistake?
Ok looks like I have found a basic solution that can be used to persist Strategy Pattern interfaces implementations. I used a #Converter annotation and a AttributeConverter class to convert strategy class names to column while saving to database and cast the retrieved String back to strategy class as following:
#Entity
public class Creature {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Convert(converter = SkillConverter.class)
private SkillInterface skill;
}
public class SkillConverter implements AttributeConverter<SkillInterface,String> {
#Override
public String convertToDatabaseColumn(SkillInterface skill) {
return skill.getClass().getSimpleName().toLowerCase();
}
#Override
public SkillInterface convertToEntityAttribute(String dbData) {
//works as a factory
if (dbData.equals("noskill")) {
return new NoSkill();
} else if (dbData.equals("axe")) {
return new Axe();
}
return null;
}
}
public interface SkillInterface {
public String getSkill();
void attack();
}
public class NoSkill implements SkillInterface{
public String getSkill() {
return getClass().getSimpleName();
}
#Override
public void attack() {
//strategy statements
}
}
You can use a proxy field to this for you like below:
abstract class Creature {
#Column
private String name;
// strategy pattern composition
private SkillInterface skill;
#Column
private String skillName;
public String getSkillName() {
return skill.getClass().getSimpleName();
}
public void setSkillName(String skillName) {
//ignore
}
}
Is there a way to write and register a TupleConverter converter in Spring Data? I'm getting this exception when I have an #Query annotation in the Repository interface and asking for Projection.
The Interface:
public interface ProjectRepository extends JpaRepository<Project, Integer> {
#Query("select p.projectId, p.projectName, p.techstack from Project p")
public List<ProjectItem> findAllForTest();
}
The DTO:
public class ProjectItem {
private final Integer projectId;
private final String projectName;
private final String techstack;
#JsonCreator
public ProjectItem(
#JsonProperty("projectId") Integer projectId,
#JsonProperty("projectName") String projectName,
#JsonProperty("techstack") String techstack
) {
this.projectId = projectId;
this.projectName = projectName;
this.techstack = techstack;
}
public Integer getProjectId() {
return projectId;
}
public String getProjectName() {
return projectName;
}
public String getTechstack() {
return techstack;
}
}
The exception
No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [project.item.ProjectItem]] with root cause
Use a JPQL Constructor Expression:
#Query("select new com.company.path.to.ProjectItem(p.projectId, p.projectName, p.techstack) from Project p")
You're close. If you just want a DTO with a few of the items from the original item, just use the interface projection technique with methods having the same signatures as the Project class method items you want:
public interface ProjectTestSummary {
public Integer getProjectId();
public String getProjectName();
public String getTechstack();
}
And in your DAO:
public interface ProjectRepository extends JpaRepository<Project, Integer> {
public List<ProjectTestSummary> findAllProjectedBy();
}
I'm trying to deserialize JSON Array, which is persisted into my MongoDB, to a Java object by using Jackson. I found many tutorials mentioned to handle this polymorphism by adding:
#JsonTypeInfo(use=Id.CLASS,property="_class")
to a Super-class. However, in my case, I can't be able to modify the Super-class. So, are there some solutions to solve it without modifying the Super-class? Here is my code:
public class User {
#JsonProperty("_id")
private String id;
private List<Identity> identities; // <-- My List contains objects of an abstract class; Identity
public User(){
identities = new ArrayList<Identity>();
}
public static Iterable<User> findAllUsers(){
return users().find().as(User.class); // Always give me the errors
}
/*More code*/
}
It always give me the error - Can not construct instance of securesocial.core.Identity, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.
You can use #JsonDeserilize annotation to bind a concrete implementation class to an abstract class. If you cannot modify your abstract class you can use the Jackson Mix-in annotations to tell Jackson how to find the implementation class.
Here is an example:
public class JacksonAbstract {
public static class User {
private final String id;
private final List<Identity> identities;
#JsonCreator
public User(#JsonProperty("_id") String id, #JsonProperty("identities") List<Identity> identities) {
this.id = id;
this.identities = identities;
}
#JsonProperty("_id")
public String getId() {
return id;
}
public List<Identity> getIdentities() {
return identities;
}
}
public static abstract class Identity {
public abstract String getField();
}
#JsonDeserialize(as = IdentityImpl.class)
public static abstract class IdentityMixIn {
}
public static class IdentityImpl extends Identity {
private final String field;
public IdentityImpl(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String getField() {
return field;
}
}
public static void main(String[] args) throws IOException {
User u = new User("myId", Collections.<Identity>singletonList(new IdentityImpl("myField")));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Identity.class, IdentityMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(u);
System.out.println(json);
System.out.println(mapper.readValue(json, User.class));
}
}