Java entity to map

The Techno Journals

It’s very common to have the DTO class for a given entity in any application. When persisting data, we use entity objects and when we need to provide the data to end user/application we use DTO class. Due to this we may need to have similar properties on DTO class as we have in our Entity class and to share the data we populate DTO objects using entity objects. To do this we may need to call getter on entity and then setter on DTO for the same data which increases number of code line. Also if number of DTOs are high then we need to write lot of code to just get and set the values or vice-versa.
To overcome this problem we are going to use Jackson API and will see how to do it with minimal code only.

Maven dependency

 com.fasterxml.jackson.core jackson-databind 2.9.9  

Entity class

@Entity @Table(name = "EMPLOYEE") public class Employee < @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "name") private String name; @Column(name = "age") private Integer age; public Employee() <>public Employee(String name, Integer age) < super(); this.name = name; this.age = age; >//getter methods //setter methods >

DTO Class

Below is our DTO class which contains @JsonIgnoreProperties annotation to ignore the Employee instance properties during conversion which are not available in EmpDto class. Also EmptDto can have extra fields which are not available in entity class. I have not create the property for «age» in DTO which is there in entity class to showcase this scenario. Also it has «dept» property which is not there in entity class.

@JsonIgnoreProperties(ignoreUnknown = true) class EmpDto < private String name; private long id; private String dept = "IT"; public String getName() < return name; >public void setName(String name) < this.name = name; >//getter methods //setter methods public String toString() < return "ID: " + id + ", Dept: " + dept + ", Name: " + name; >>

Conversion code

Below generic method is created using Jackson API which can be used for any type of class conversion.

public T convertObjToXXX(Object o, TypeReference ref) 

Entity class conversion and execution

Now we will create the object of Employee class (Entity) and convert it to multiple types like Map, Properties and DTO class (can be any POJO class).

//Create entity object to be converted Employee emp = new Employee("Emp-1", 30); emp = empRepository.save(emp);//saving to populate the id field //Convert to Map class Map map = convertObjToXXX(emp, new TypeReference()<>); System.out.println("Convert to Map :"); System.out.println(map); //Convert to Properties class Properties props = convertObjToXXX(emp, new TypeReference()<>); System.out.println("\nConvert to Properties :"); System.out.println(props); //Convert to DTO class EmpDto dto = convertObjToXXX(emp, new TypeReference()<>); System.out.println("\nConvert to DTO :"); System.out.println(dto);

Output:

Convert to Map : Convert to Properties :  

You may check my another post where I have explained how to use annotation processor to generate the DTO classes using Entity class at compile time. Link is given below.
https://www.thetechnojournals.com/2019/12/annotation-processor-to-generate-dto.html

  • Get link
  • Facebook
  • Twitter
  • Pinterest
  • Email
  • Other Apps

Источник

How to map an association as a java.util.Map

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, 2 monthly Q&A calls, monthly coding challenges, a community of like-minded developers, and regular expert sessions.

The java.util.List is the most common representation of a to-many association with JPA and Hibernate. But is it also the one that you want to use in your domain model? Does it fit your use cases or do you need smarter access to the associated elements?

Let’s be honest, a simple java.util.List is good enough in most cases. But from time to time, a java.util.Map would make the implementation of your business logic so much easier.

So, why not use JPA’s and Hibernate’s mapping capabilities and map the association to a Map? Only for the relationships that are used as Maps, of course. The mapping definition requires just a few additional annotations.

Mapping simple key-value pairs

The basic mappings are pretty simple. JPA and Hibernate allow you to map a Collection of elements or an entity association to a java.util.Map. The key and value of the Map can be a basic type, an embeddable class or an entity.

You might have recognized that Collections are not supported. If you want to map your association to a Map>, you need to use a small workaround that I show you at the end of this post.

But let’s take a look at some simple mapping first and discuss the mapping concept.

Represent a to-Many Association as a Map

Let’s start with a standard mapping definition of a many-to-many association that uses a List. The following diagram shows an example of a many-to-many association between the Author and the Book.

Author-Book

And you can see the standard mapping using a List in the following code snippet. If you’re not familiar with the mapping of one-to-many or many-to-many associations, you should take a look at my Ultimate Guide to Association Mappings with JPA and Hibernate before you continue.

@Entity public class Author < @ManyToMany @JoinTable( name="BookAuthor", joinColumns=<@JoinColumn(name="bookId", referencedColumnName="id")>, inverseJoinColumns=<@JoinColumn(name="authorId", referencedColumnName="id")>) private List books = new ArrayList(); . >

If you want to represent the association with a Map instead of the List, you just have to do 2 things:

  1. change the type of the attribute from List to Map and
  2. define the database column you want to use as a map key.

Let’s say you want to use the title column of the Book table as the map key. The title is a String attribute of the Book. So, it’s a basic type that’s already mapped by your entities and you just need to reference it in a @MapKey annotation.

If you don’t want to use one of the attributes of the associated entity, you can also use:

  • a basic type with a @MapKeyColumn annotation
  • an enum with a @MapKeyEnumerated annotation
  • a java.util.Date or a java.util.Calendar with a @MapKeyTemporal annotation

Hibernate will persist the map key in an additional database column.

But back to our example. When you apply the described changes, the mapping of the Author entity looks like this.

@Entity public class Author < @ManyToMany @JoinTable( name="AuthorBookGroup", joinColumns=<@JoinColumn(name="fk_author", referencedColumnName="id")>, inverseJoinColumns=<@JoinColumn(name="fk_group", referencedColumnName="id")>) @MapKey(name = "title") private Map books = new HashMap(); . >

Working with Maps instead of Lists

That’s all you need to do to define the mapping. You can now use the map in your domain model to add, retrieve or remove elements from the association.

Author a = new Author(); a.setFirstName("Thorben"); a.setLastName("Janssen"); em.persist(a); Book b = new Book(); b.setTitle("Hibernate Tips"); b.setFormat(Format.PAPERBACK); b.getAuthors().add(a); em.persist(b); a.getBooks().put(b.getTitle(), b);

You also have 2 options to use the association in JPQL query. You can simply join the 2 entities and use them as a regular many-to-many relationship.

TypedQuery q = em.createQuery("SELECT a FROM Author a JOIN a.books b WHERE b.title LIKE :title", Author.class); q.setParameter("title", "%Hibernate Tips%"); a = q.getSingleResult();

Or you can use the KEY function to reference the map key in your query.

TypedQuery q = em.createQuery("SELECT a FROM Author a JOIN a.books b WHERE KEY(b) LIKE :title ", Author.class); q.setParameter("title", "%Hibernate Tips%"); a = q.getSingleResult();

Represent a Collection of Embeddables as a Map

The mapping for embeddable classes is pretty similar. The main difference is that you need to use the @ElementCollection annotation instead of a @ManyToMany annotation.

Let’s say you want to persist an author with her business and private address. In your domain model, you might model that with the 3 classes you can see in the following diagram.

Author-Address-AddressType

The Author class becomes an entity and you can implement the Address class as an embedabble. And for this example, I don’t want to keep the Addresses in a simple ElementCollection. I want to use a Map with the AddressType enum.

As in the previous example, you only need to do 2 things to use a Map instead of a List:

The second step even becomes optional if you only use the enum as a map key and not as an attribute of the embeddable. If you don’t provide any additional information, Hibernate uses a set of defaults to persist the map key in a column of the collection table.

I prefer to define the column name and the EnumType Hibernate shall use to persist the enum. You can see an example for that in the following code snippet. The name attribute of the @MapKeyColumn annotation defines the name of the database column and the @MapKeyEnumerated defines how Hibernate shall persist the enum.

@Entity public class Author < @ElementCollection @MapKeyColumn(name = "address_type") @MapKeyEnumerated(EnumType.STRING) private Mapaddress = new HashMap(); . >

That’s all you have to do to represent a collection of embeddables as a java.util.Map. You can now use it in the same way as I showed you in the previous example.

Mapping multiple values to the same key

As you’ve seen in the previous examples, JPA and Hibernate don’t support any Collection type as a map value. That’s unfortunate because, in my experience, that is the most common use case.

But there is a small and easy workaround for these situations. You just need to introduce a wrapper entity that wraps the List or Map of entities.

Let’s extend the example of the AuthorBook-association I showed you at the beginning of this post.

Most Books get published in different Formats, e.g. as an ebook and as a paperback. The Book has a unique ISBN for each Format. So, you need 2 database records to persist the ebook and paperback version of a Book.

But you can’t persist these 2 records with the mapping from our first example. The 2nd Book entity would replace the 1st one, when you add it to the Map books.

You can fix that by introducing an additional entity that wraps the List of Book entities.

BookGroup

The mapping definition of that entity is very simple. You just need a primary key and a one-to-many association to the Book entity. I also introduced a title for the BookGroup which I want to use as the map key on the Author entity.

@Entity public class BookGroup < @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id; @OneToMany(mappedBy = "group") private Listbooks = new ArrayList(); private String title; . >

You can then model the association between the BookGroup and the Author entity as a Map.

@Entity public class Author < @ManyToMany @JoinTable( name="AuthorBookGroup", joinColumns=<@JoinColumn(name="fk_author", referencedColumnName="id")>, inverseJoinColumns=<@JoinColumn(name="fk_group", referencedColumnName="id")>) @MapKey(name = "title") private Map bookGroups = new HashMap(); . >

That’s all you need to do to model an association as a Map with multiple values per map key. You can now use it in the same way as I showed you in the first example.

Please be aware, that this mapping creates overhead and redundancy. The BookGroup entity gets mapped to a database table which we didn’t need in the previous examples. And I now use the title of the BookGroup as the key of the Map bookGroups. The BookGroup title will be the same as the title of all Books in the group.

Summary

As you’ve seen, JPA and Hibernate also allow you to represent an association as a java.util.Map. That can be beneficial when you need more sophisticated access to the related entities. But please be aware, that the representation as a Map introduces an overhead. You should, therefore, only use it, when you really need the Map representation.

The mapping definition is pretty easy. You just define the association between your entities, use a Map as the attribute type and define the map key with a @MapKey, @MapKeyColumn, @MapKeyEnumerated or @MapKeyTemporal annotation.

Источник

Читайте также:  Object detection python install
Оцените статью