Site icon JVM Advent

Power Up Your Model With Spring Data 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:

User.java
@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:

UserRepository.java
@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 result 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 Of 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:

UserProjection.java
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:

UserRepository.java
@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’m a Senior Consultant at Valtech Ltd. and work on various projects in a wide range of roles. Lately, I was the tech lead for a .net and a Java project. Technology wise I am interested in NoSQL technologies and polyglot JVM development.

Author: Mani Sarkar

I have been posting articles on the Java Advent calendar for the last two years.

A Java developer at heart, I have been involved with the Adopt programs (i.e. @adoptopenjdk & @adoptajsr) since its inception.

Strongly prescribe to Agile (TDD, BDD), and Software Craftsmanship thinking, and hence working for Codurance where I get to learn about the principles and practices that go with it.

Active member of the London Java Community and the London Software Craftsmanship Community.

Among other things I’m a speaker, write blog posts now and then, constantly looking to improve myself and learning new things (Java and other things not Java). You will find me at many events and conferences both in London and other parts of Europe.

Shameless plug: learn all about Adopt OpenJDK, see http://bit.ly/1NUkPWw and http://www.slideshare.net/neomatrix369/.

—–
Twitter: @theNeomatrix369
—–

Exit mobile version