In scenarios where direct database access is unavailable, Hibernate provides a powerful feature to define views at the application level. This approach allows developers to create virtual entities that map to custom query definitions without requiring actual database view creation.
Core Annotations
Hibernate offers three key annotations for implementing application-level views:
@Immutable- Marks the entity as read-only, preventing any modification operations@Subselect- Embeds a SQL subquery that defines the view's data source@Synchronize- Specifies which tables the subquery references, enabling automatic cache invalidation
Implementation Example
import javax.persistence.Entity; import javax.persistence.Id;
@Entity @org.hibernate.annotations.Immutable @org.hibernate.annotations.Subselect( value = "SELECT e.id AS entityId, " + "CONCAT(e.employee_name, '-profile') AS displayName " + "FROM Employee e" ) @org.hibernate.annotations.Synchronize({"Employee"}) public class EmployeeProfileView {
@Id
protected long entityId;
protected String displayName;
public long getEntityId() {
return entityId;
}
public void setEntityId(long entityId) {
this.entityId = entityId;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
@Override
public String toString() {
return "EmployeeProfileView [entityId=" + entityId + ", displayName=" + displayName + "]";
}
}
</div>Testing the View
----------------
<div>```
package com.example.persistence;
import static org.junit.Assert.assertNotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class EmployeeProfileViewTest {
private Session currentSession;
public EmployeeProfileViewTest(SessionFactory factory) {
this.currentSession = factory.openSession();
}
@Parameters
public static Collection sessionFactories() {
return Arrays.asList(new Object[]{TestEnvironment.getFactory()});
}
@Before
public void initialize() {
currentSession.beginTransaction();
}
@After
public void cleanup() {
currentSession.getTransaction().commit();
}
@Test
public void verifyViewDataRetrieval() {
List results = currentSession
.createQuery("SELECT v FROM EmployeeProfileView v")
.getResultList();
for(Object item : results) {
System.out.println(item);
}
assertNotNull(results);
}
}
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.auto_quote_keyword">true</property>
<!--
Schema generation strategy:
create - Drop and recreate tables on each session factory build
create-drop - Same as create, plus automatic table deletion on shutdown
update - Maintain schema incrementally, preserve existing data
validate - Verify schema compatibility without modifications
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping class="com.example.persistence.entity.Employee"/>
<mapping class="com.example.persistence.entity.Department"/>
<mapping class="com.example.persistence.entity.EmployeeProfileView"/>
</session-factory>