JVM Advent

The JVM Programming Advent Calendar

Power Up Your Data Model With Projections

Introduction

Data models can be tricky. Modelling can be even harder. Sometimes information that should go into a database table isn’t necessarily what we want to go out to every piece of code.

And like so many other times, Spring comes to the rescue. A little feature called projection helps us to map data with only a few lines in an ordinary interface.

In this article, we are going to see a simple example of how we can use projections.

The Basics

OK, let’s set the scene. Imagine we have the following entity:

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
@EqualsAndHashCode(doNotUseGetters = true)
@ToString(doNotUseGetters = true)
public class User implements Serializable {
 
 @Id
 @SequenceGenerator(name = "user_seq", sequenceName = "user_seq")
 @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "user_seq")
 private Long id;
 
 @Basic
 private String username;
 
 @Basic
 private String salt;
 
 @Basic
 private String password;
 
 @Basic
 private String firstName;
 
 @Basic
 private String lastName;
}

Some explanation might be helpful here: Let’s have a look at the annotations. I am lazy, honestly, so Lombok is right up my alley. Lombok gives us a nice declarative way to say we need:

  • a nice builder interface to create the bean (@Builder)
  • Getters and setter (@Data)
  • a default constructor (@NoArgsConstructor)
  • one more constructor with arguments for all fields (@AllArgsConstructor)
  • equals() and hashCode(), but please use the fields, not the getters (@EqualsAndHashCode(doNotUseGetters = true))
  • toString(); again, use the fields (@ToString(doNotUseGetter = true))

The remaining annotations (@Entity and @Table) are good old JPA.

Right, so, we have a nice entity. What’s the issue?

Get Data The Traditional way

Let’s have a look at this repository:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}

The above code provides us with a minimal set of CRUD methods. One is getOne(Long id). Good, isn’t it?

Well, the correct answer must be: It depends! Why? Because this returns the whole entity, including the salt and the hashed password. This is very sensitive information. Especially the salt should never be available to the outside world.

In order to get this information out of the resulting entity, we would have to do a lot of manual work. Just from the top of my head, we should: * create a new bean * implement a mapper to get from our entity to the new bean * make sure every time we deal with that entity, we also map it * getting headaches when realising there are also multiple results possible.

Return The Minimum Necessary

Thankfully, Spring safes the day. A little feature called Projections lets us define the mapping in a declarative way. Such an interface could look like that:

public interface UserProjection {
 
 @Value("#{target.getUsername()}")
 String getUsername();
 
 @Value("#{target.getFirstName()}")
 String getFirstName();
 
 @Value("#{target.getLastName()}")
 String getLastName();
}

Spring will replace target with the entity we are currently dealing with. In other words, target will be an instance of User.

The only thing we have to do now is something like this:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
 
 UserProjection findById(Long id);
 
 List<UserProjection> findAllUser();
}

Now, every time we call findById(), we will get an instance of UserProjection. No leakage of our salt or password hash possible! Even better, we can use the same procedure for methods with multiple results.

Conclusion

We can save a lot of code and pain with Spring Projections. And the @Value() definitions can get as complex as we need it. In my current project, for example, this saves my team a lot of boilerplate code when we map an “interesting” legacy database design into easier data models.

If you want to give this a spin, you can find a simple example application on GitHub.

Author: Holger Steinhauer

I am a passionate software developer with a DevOps mindset. I love all things Kotlin and have more than 15 years of experience in the market. Besides coding, I run the Virtual Kotlin User Group, co-organise the Kotlin User Group Berlin, started my own podcast Coding with Holger and founded the digital IT consultancy for the 21st century.

Next Post

Previous Post

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2024 JVM Advent | Powered by steinhauer.software Logosteinhauer.software

Theme by Anders Norén