I'm attempting to create a JPA entity for a view. From the database layer, a table and a view should be the same.
However, problems begin to arise and they are two fold:
When attempting to setup the correct annotations. A view does not have a primary key associated with it, yet without the proper @javax.persistence.Id
annotated upon a field, you will get an org.hibernate.AnnotationException: No identifier specified for entity
thrown at Runtime.
The Spring Boot JpaRepository
interface definition requires that the ID
type extends Serializable
, which precludes utilizing java.lang.Void
as a work-around for the lack of an id on an view entity.
What is the proper JPA/SpringBoot/Hibernate way to interact with a view that lacks a primary key?
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.
In JPA you can map to a VIEW the same as a table, using the @Table annotation. You can then map each column in the view to your object's attributes. Views are normally read-only, so object's mapping to views are normally also read-only.
If we want to use JPA with MySQL database, we need the mysql-connector-java dependency. We'll also need to define the DataSource configuration. We can do this in a @Configuration class or by using standard Spring Boot properties. Spring Boot will automatically configure a data source based on these properties.
Spring Data JPA focuses on using JPA to store data in a relational database. Its most compelling feature is the ability to create repository implementations automatically, at runtime, from a repository interface. CustomerRepository extends the CrudRepository interface.
I was exploring that topic too. I ended up using Spring Data JPA Interface-based Projections with native queries.
I created an interface, making sure the UPPERCASE part matches the DB Column names:
public interface R11Dto { String getTITLE(); Integer getAMOUNT(); LocalDate getDATE_CREATED(); }
Then i created a repository, for an Entity (User) not related in any way to the view. In that repository i created a simple native query. vReport1_1 is my view.
public interface RaportRepository extends JpaRepository<User, Long> { @Query(nativeQuery = true, value = "SELECT * FROM vReport1_1 ORDER BY DATE_CREATED, AMOUNT") List<R11Dto> getR11(); }
1. Create View with native SQL in the database,
create or replace view hunters_summary as select em.id as emp_id, hh.id as hh_id from employee em inner join employee_type et on em.employee_type_id = et.id inner join head_hunter hh on hh.id = em.head_hunter_id;
2. Map that, View to an 'Immutable Entity'
package inc.manpower.domain; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Subselect; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; import java.util.Date; @Entity @Immutable @Table(name = "`hunters_summary`") @Subselect("select uuid() as id, hs.* from hunters_summary hs") public class HuntersSummary implements Serializable { @Id private String id; private Long empId; private String hhId; ... }
3. Now create the Repository with your desired methods,
package inc.manpower.repository; import inc.manpower.domain.HuntersSummary; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; import javax.transaction.Transactional; import java.util.Date; import java.util.List; @Repository @Transactional public interface HuntersSummaryRepository extends PagingAndSortingRepository<HuntersSummary, String> { List<HuntersSummary> findByEmpRecruitedDateBetweenAndHhId(Date startDate, Date endDate, String hhId); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With