Spring Data JPA – Working with Views
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
Database administrators often use views to combine, transform and present data in a form that’s easy to process and query by the application. They are easier to maintain and optimize than a complex query statement that’s developed and maintained in one or more applications. Using Spring Data JPA, you can map and query views in almost the same way as database tables.
If you compare an SQL statement that selects data from a view with one that selects the same data from a table with the same structure, you will not see any difference. Because of that, you can use your regular set of annotations to model an entity that maps your view. And in your queries, you can use it in the same way as any other entity.
But depending on how you defined your view and the capabilities of your RDBMS, the view might not support write operations. In that case, I recommend modeling an immutable entity and a read-only repository. It not only prevents all write operations but also improves the maintainability and performance of your application.
Modelling an Immutable Entity
If you can’t update the data in your view, you should model a read-only entity. As I explained in my Hibernate Tip on mapping views, this enables your persistence provider to apply internal performance optimizations, e.g., exclude the entity objects from all dirty checks.
You define an immutable entity in almost the same way as any other entity. You can use the same mapping annotations, and you, of course, need to fulfill JPA’s requirements of an entity class. It has to be a public, non-final class with a default constructor and non-final attributes. You also need to define at least 1 attribute that identifies each object.
In addition to all of that, you need to annotate your class with @Immutable. This annotation tells Hibernate that the entity object will not change. Hibernate will exclude it from all dirty checks and will not trigger any SQL UPDATE statements.
But be warned: Hibernate doesn’t ensure that you don’t change any entity attributes. It will just not trigger any SQL UPDATE statements. You should therefore not provide any setter methods.
@Entity @Immutable public class ChessGameView { @Id private Long id; private int gameRound; private String tournamentName; public Long getId() { return id; } public int getGameRound() { return gameRound; } public String getTournamentName() { return tournamentName; } }
Making your entity class immutable is the most important step to working with database views in Spring Data JPA. Based on this mapping, Hibernate will ensure that your application doesn’t perform any write operations on the view. To make your persistence layer easier to use, you should take this one step further and also exclude all methods from your repository that persist, update or remove an entity.
Defining a Read-Only Repository
All standard repository definitions provided by Spring Data JPA support read and write operations. If you want to create a read-only repository, you need to define it yourself. But don’t worry, you don’t need to provide any implementation for the read operation. You only need to define an interface, and you can use all standard methods provided by one of Spring Data JPA’s standard repositories.
I always like to define my read-only repositories in 2 steps. I first create a reusable ReadOnlyRepository definition that I then extend and customize for each immutable entity class.
A Generic Read-Only Repository
The 2 things you need to do to create your own repository definition are defining an interface that extends the Repository interface and copying a few method definitions from Spring Data JPA’s standard repositories.
In the following example, I copied the findAll() method from the JpaRepository, the findAll(Sort sort) and findAll(Pageable pageable) methods from the PagingAndSortingRepository and the findById(ID id) and count() methods from the CrudRepository interface.
@NoRepositoryBean public interface ReadOnlyRepository<T, ID> extends Repository<T, ID> { List<T> findAll(); List<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); Optional<T> findById(ID id); long count(); }
As you can see in the code snippet, I didn’t specify the generic types in my ReadOnlyRepository definition. We do that in the next step when we create the entity-specific repository versions. And I also annotated the interface with @NoRepositoryBean. That annotation tells Spring Data JPA not to create an instance of this interface.
An Entity-Specific Read-Only Repository
Based on the ReadOnlyRepository definition, you can then define your entity-specific read-only repositories. To do that, you extend the interface definition in the same way as you extend any of Spring Data JPA’s standard repository definitions.
public interface ChessGameViewRepository extends ReadOnlyRepository<ChessGameView, Long> { List<ChessGameView> findByTournamentName(String tournamentName); }
As you can see in the code snippet, I specified the type of the entity class and of the primar key attribute. And I also added a derived query that returns all games played in a tournament with the provided name.
In your application, you can then use the repository in the same way as any of your other repositories. The only difference you will recognize is that the ChessGameViewRepository only provides the findByTournamentName and the 5 methods inherited from the ReadOnlyRepository.
Conclusion
You can read data from views in the same way as you read it from a database table. Because of that, it’s no surprise that you can map them in almost the same way. The main difference is that views are often read-only, and you need to prevent any write operations.
You can do that by annotating your entity class with @Immutable and defining a custom, read-only repository.
The annotation tells Hibernate to exclude all objects of that entity class from all dirty checks. Due to that, it will not trigger any SQL UPDATE statements.
And you can define a read-only repository by extending the Repository interface and copying some method definitions from Spring Data JPA’s standard repositories. Spring provides an implementation of your repository definition and all its standard methods. You can also use derived queries to let Spring generate and execute custom queries.