Hibernate Subselect Annotation
1. Introduction
Hibernate is an open source object relational mapping (ORM) tool that provides a framework to map object-oriented domain models to relational databases for web applications. The @org.hibernate.annotations.Subselect annotation is a Hibernate-specific feature and is used to map an immutable entity to a SQL subselect statement. Hibernate does not support insert, update, or delete operations on these immutable entities. Here is the Hibernate subselect annotation definition:
@Subselect Definition
/**
* Maps an immutable and read-only entity to a given SQL {@code select} expression.
*
* This is an alternative to defining a database view and mapping the entity to
* the view using the {@link jakarta.persistence.Table @Table} annotation.
*
* @see Synchronize
*
* @author Sharath Reddy
*/
@Target(TYPE)
@Retention(RUNTIME)
public @interface Subselect {
/**
* The query.
*/
String value();
}
As you see from the definition. @Subselect is an alternative way to map an entity to a database view or subquery. In this example, I will create a spring boot web application which relies on two hibernate entities: Customer and Order and two immutable entities: CustomerOrder and MajorCustomer.
2. Set up Maven Spring Boot Web Application
Navigate to Spring Initializr, select Maven, Java, and “Spring Data JPA”, “H2 Database”, and “Spring Web” dependencies as the following screenshot:
Imported the generated maven spring boot project into Eclipse IDE. Verified the pom.xml as the following.
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.0</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>org.zheng.demo</groupId> <artifactId>sebselect-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>sebselect-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3. Customer and Order Entities
In this example, I will create two entities and map them to two tables.
3.1 Customer Entity
In this step, I will create a Customer class which annotates with the @Entity annotation. The Customer class has five members:
emailAddress– the customer email address.id– the primary key of the customer.name– the customer’s name.type– the customer type. If the customer’s order amount exceeds 5000, then it is a major customer.orders– a list of orders belong to this customer.
Customer.java
package org.zheng.demo.entity;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
@Entity(name = "T_Customer")
public class Customer {
private String emailAddress;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@OneToMany(mappedBy = "customer")
private List<Order> orders;
private String type;
public Customer() {
super();
}
public Customer(String name, String emailAddress) {
super();
this.name = name;
this.emailAddress = emailAddress;
}
public String getEmailAddress() {
return emailAddress;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public List<Order> getOrders() {
return orders;
}
public String getType() {
return type;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Customer [emailAddress=" + emailAddress + ", id=" + id + ", name=" + name + ", type=" + type + "]";
}
}
- Line 11: annotates with
@Entityand maps to H2 tableT_Customer. - Line 15, 16: annotates with
@Idand@GeneratedValuethat auto generates the primary unique id. - Line 21: annotates with
@OneToManythat sets up a 1-to-many relationship betweenCustomerandOrder.
3.2 Order Entity
In this step, I will create an Order class which annotates with the @Entity annotation. The Order class has the following members:
amount– the order amount.id– the primary key of the order.name– the order name.customer– the customer that the order belongs to.quantity– the order quantity.
Order.java
package org.zheng.demo.entity;
import java.math.BigDecimal;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
@Entity(name = "T_Order")
public class Order {
private BigDecimal amount;
@ManyToOne
@JoinColumn(name = "cust_id")
private Customer customer;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int quantity;
public Order() {
super();
}
public Order(String name, int quantity, BigDecimal amount, Customer customer) {
super();
this.name = name;
this.quantity = quantity;
this.customer = customer;
this.amount = amount;
}
public BigDecimal getAmount() {
return amount;
}
public Customer getCustomer() {
return customer;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public int getQuantity() {
return quantity;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
@Override
public String toString() {
return "Order [customer=" + customer + ", id=" + id + ", name=" + name + ", quantity=" + quantity + ", amount="
+ amount + "]";
}
}
- Line 12: annotates with
@Entityand map to H2 tableT_Order. - Line 16, 17: annotates with
@ManyToOneand@JoinColumnto define the foreign key column:cust_id. - Line 20, 21: annotates with
@Id,@GeneratedValuefor the primary key.
3.3 Customer Repository
In this step, I will create a CustomerRepo interface which extends from JpaRepository.
CustomerRepo.java
package org.zheng.demo.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zheng.demo.entity.Customer;
public interface CustomerRepo extends JpaRepository<Customer, Long> {
}
- Line 6: extends from
JpaRepository.
3.4 Order Repository
In this step, I will create an OrderRepo interfaceclass which extends from JpaRepository.
OrderRepo.java
package org.zheng.demo.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zheng.demo.entity.Order;
public interface OrderRepo extends JpaRepository<Order, Long> {
}
- Line 6: extends from
JpaRepository.
4. Immutable Entities
In this example, I will create two immutable entities and map to two subqueries.
4.1 CustomerOrder
In this step, I will create an immutable entity CustomerOrder class with the @Entity, @Immutable, and @Subselect annotations. Here is the sub query used with the @Subselect.
Customer Order SubQuery
SELECT t_customer.id id,
t_customer.NAME NAME,
a.total total
FROM t_customer,
(SELECT c.id,
Sum(o.amount) total
FROM t_customer c,
t_order o
WHERE c.id = o.cust_id
GROUP BY c.id) a
WHERE a.id = t_customer.id This subquery returns the customer along with its order’s total amount.
CustomerOrder.java
package org.zheng.demo.subselect;
import java.math.BigDecimal;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Subselect;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
@Immutable
@Subselect("select t_customer.id id, t_customer.name name, a.total total " + "from t_customer ,"
+ "( select c.id, sum(o.amount) total " + "from t_customer c, t_order o " + "where c.id = o.cust_id "
+ "group by c.id " + ") a where a.id = t_customer.id")
public class CustomerOrder {
@Id
@Column(name = "id")
private Long id;
@Column(name = "name")
private String customerName;
@Column(name = "total")
private BigDecimal totalOrderAmount;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public BigDecimal getTotalOrderAmount() {
return totalOrderAmount;
}
public void setTotalOrderAmount(BigDecimal totalOrderAmount) {
this.totalOrderAmount = totalOrderAmount;
}
}
- Line 12,13,14: creates an immutable entity with
@Entity,@Immutable, and@Subselect. Note, the subselect query is the SQL query. - Line 20, 23, 26: maps the immutable entity to the subselect query’s columns.
4.2 MajorCustomer
In this step, I will create an immutable entity MajorCustomer class which represents a customer whose type equals “Major“.
Major Customer SubQuery
SELECT id,
NAME,
email_address AS email
FROM t_customer
WHERE type = 'Major' This subquery returns the customer whose type = ‘Major‘.
MajorCustomer.java
package org.zheng.demo.subselect;
import org.hibernate.annotations.Subselect;
import org.springframework.data.annotation.Immutable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
@Immutable
@Subselect("SELECT id, name, email_Address as email FROM t_customer where type= 'Major' ")
public class MajorCustomer {
@Column(name = "email")
private String email;
@Id
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
public String getEmail() {
return email;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setEmail(String email) {
this.email = email;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
- Line 10, 11, 12: creates an immutable
MajorCustomerentity whose type equals “Major“. - Line 19, 22: maps the immutable entity to the subselect query’s columns.
4.3 CustomerOrderService
In this step, I will create a CustomerOrderService class which returns a list of CustomerOrder that contains customer data and its orders’ total amount.
CustomerOrderService.java
package org.zheng.demo.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.zheng.demo.subselect.CustomerOrder;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
@Service
public class CustomerOrderService {
private EntityManager em;
public CustomerOrderService(EntityManager em) {
super();
this.em = em;
}
public List<CustomerOrder> getCustomerOrders() {
TypedQuery<CustomerOrder> ret = em.createQuery("select o from CustomerOrder o", CustomerOrder.class);
return ret.getResultList();
}
}
- Line 16: inject
EntityManager. - Line 22: create a JPQL query to access the immutable
CustomerOrderentity and return the results.
4.4 MajorCustomerService
In this step, I will create a MajorCustomerService class which returns a list of major customers.
MajorCustomerService.java
package org.zheng.demo.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.zheng.demo.subselect.MajorCustomer;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
@Service
public class MajorCustomerService {
private EntityManager entityManager;
public MajorCustomerService(EntityManager entityManager) {
this.entityManager = entityManager;
}
public List<MajorCustomer> getMajorCustomers() {
TypedQuery<MajorCustomer> query = entityManager.createQuery("SELECT o FROM MajorCustomer o",
MajorCustomer.class);
return query.getResultList();
}
}
- Line 21: returns a list of
MajorCustomerfrom the immutable entity.
5. Web Service
In this step, I will create two Restful endpoints which utilize the data and services created at step 3 and 4 to return a list of major customer and customer’s order total amounts.
5.1 ReportRest
In this step, I will create a ReportRest class which has two get endpoints.
ReportRest.java
package org.zheng.demo.rest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zheng.demo.service.MajorCustomerService;
import org.zheng.demo.service.CustomerOrderService;
import org.zheng.demo.subselect.MajorCustomer;
import org.zheng.demo.subselect.CustomerOrder;
@RestController
public class ReportRest {
@Autowired
private MajorCustomerService custService;
@Autowired
private CustomerOrderService opService;
@GetMapping("/majorCustomers")
public List getMajorCustomers() {
return custService.getMajorCustomers();
}
@GetMapping("/customerOrders")
public List getOrderReport() {
return opService.getCustomerOrders();
}
}
- Line 22: defines a
GetMappingto “/majorCustomers” to return a list of major customers. - Line 27: defines a
GetMappingto “/customerOrders” to return a list of customers with its total order amount.
5.2 SebselectDemoApplication
In this step, I will update the generated SebselectDemoApplication class to insert five customers and 10 orders and calculate the customer’s order total amount and set the customer as a major customer if its total order amount exceeds 5000.
SebselectDemoApplication.java
package org.zheng.demo;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Optional;
import java.util.Random;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.zheng.demo.entity.Customer;
import org.zheng.demo.entity.Order;
import org.zheng.demo.repo.CustomerRepo;
import org.zheng.demo.repo.OrderRepo;
import org.zheng.demo.service.MajorCustomerService;
import org.zheng.demo.service.CustomerOrderService;
@SpringBootApplication
public class SebselectDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SebselectDemoApplication.class, args);
}
@Bean
public CommandLineRunner demo(CustomerRepo custRep, OrderRepo orderRep, CustomerOrderService orService,
MajorCustomerService ms) {
return (args) -> {
// save a few customers
String[] custs = { "mary", "zheng", "john", "bob", "terry" };
String[] orders = { "PC", "Phone", "Laptop", "TV", "XBox" };
BigDecimal[] amount = { new BigDecimal(1000), new BigDecimal(500), new BigDecimal(800),
new BigDecimal(3000), new BigDecimal(600) };
Arrays.asList(custs).stream().forEach(cust -> {
Customer customer = new Customer(cust, cust + "@test.com");
custRep.save(customer);
Random random = new Random();
int randomNumber = random.nextInt(orders.length);
Order order = new Order(orders[randomNumber], randomNumber, randomNumber == 0 ? amount[randomNumber]
: amount[randomNumber].multiply(new BigDecimal(randomNumber)), customer);
orderRep.save(order);
randomNumber = random.nextInt(orders.length);
order = new Order(orders[randomNumber], randomNumber, randomNumber == 0 ? amount[randomNumber]
: amount[randomNumber].multiply(new BigDecimal(randomNumber)), customer);
orderRep.save(order);
});
orderRep.findAll().forEach(order -> {
System.out.println(order.toString());
});
orService.getCustomerOrders().stream().filter(orRe -> orRe.getTotalOrderAmount().intValue() > 5000)
.forEach(major -> {
Optional<Customer> majorCust = custRep.findById(major.getId());
if (majorCust.isPresent()) {
Customer major2 = majorCust.get();
major2.setType("Major");
custRep.save(major2);
}
});
System.out.println("Major customers with total exceed 5000.");
ms.getMajorCustomers().stream().forEach(m -> System.out.println(m.getName()));
};
}
}
- Line 28: injects the
CustomerRepo,OrderRepo,CustomerOrderService, andMajorCustomerServicecreated at earlier steps. - Line 32, 33, 34: initializes five customers’ name, product and and its sale amount.
- Line 39: saves the test customer.
- Line 45,50: saves the test orders for each customer.
- Line 55: prints out the test orders details, so we can use it to verify the data at step 6.
- Line 58: finds the major customer based on its total order amount exceeding 5000.
- Line 66: updates the customer’s type to “
Major“.
5.3 SB Application Properties
In this step, I will configure H2 datasource in application.properties.
applicatio.properties
spring.application.name=sebselect-demo spring.jpa.show-sql=true spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.h2.console.enabled=true spring.jpa.hibernate.ddl-auto=update spring.datasource.initialization-mode=always
6. Demo
Start the spring boot application, verify the application is started and capture the server log.
Server Log
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.3.0) 2024-05-25T14:05:14.845-05:00 INFO 14956 --- [sebselect-demo] [ main] o.zheng.demo.SebselectDemoApplication : Starting SebselectDemoApplication using Java 17.0.11 with PID 14956 (C:\MaryTools\workspace\sebselect-demo\target\classes started by azpm0 in C:\MaryTools\workspace\sebselect-demo) 2024-05-25T14:05:14.851-05:00 INFO 14956 --- [sebselect-demo] [ main] o.zheng.demo.SebselectDemoApplication : No active profile set, falling back to 1 default profile: "default" 2024-05-25T14:05:15.589-05:00 INFO 14956 --- [sebselect-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2024-05-25T14:05:15.650-05:00 INFO 14956 --- [sebselect-demo] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 51 ms. Found 2 JPA repository interfaces. 2024-05-25T14:05:16.244-05:00 INFO 14956 --- [sebselect-demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2024-05-25T14:05:16.254-05:00 INFO 14956 --- [sebselect-demo] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2024-05-25T14:05:16.254-05:00 INFO 14956 --- [sebselect-demo] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] 2024-05-25T14:05:16.322-05:00 INFO 14956 --- [sebselect-demo] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2024-05-25T14:05:16.324-05:00 INFO 14956 --- [sebselect-demo] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1418 ms 2024-05-25T14:05:16.369-05:00 INFO 14956 --- [sebselect-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2024-05-25T14:05:16.553-05:00 INFO 14956 --- [sebselect-demo] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:testdb user=SA 2024-05-25T14:05:16.555-05:00 INFO 14956 --- [sebselect-demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2024-05-25T14:05:16.569-05:00 INFO 14956 --- [sebselect-demo] [ main] o.s.b.a.h2.H2ConsoleAutoConfiguration : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:testdb' 2024-05-25T14:05:16.713-05:00 INFO 14956 --- [sebselect-demo] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2024-05-25T14:05:16.771-05:00 INFO 14956 --- [sebselect-demo] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 2024-05-25T14:05:16.809-05:00 INFO 14956 --- [sebselect-demo] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 2024-05-25T14:05:17.105-05:00 INFO 14956 --- [sebselect-demo] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2024-05-25T14:05:17.994-05:00 INFO 14956 --- [sebselect-demo] [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) Hibernate: create table t_customer (id bigint not null, email_address varchar(255), name varchar(255), type varchar(255), primary key (id)) Hibernate: create table t_order (id bigint not null, amount numeric(38,2), name varchar(255), quantity integer not null, cust_id bigint, primary key (id)) Hibernate: create sequence t_customer_seq start with 1 increment by 50 Hibernate: create sequence t_order_seq start with 1 increment by 50 Hibernate: alter table if exists t_order add constraint FKgt3n29ngim2bryiw3eimwjs52 foreign key (cust_id) references t_customer 2024-05-25T14:05:18.035-05:00 INFO 14956 --- [sebselect-demo] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2024-05-25T14:05:18.309-05:00 WARN 14956 --- [sebselect-demo] [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2024-05-25T14:05:18.679-05:00 INFO 14956 --- [sebselect-demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' 2024-05-25T14:05:18.690-05:00 INFO 14956 --- [sebselect-demo] [ main] o.zheng.demo.SebselectDemoApplication : Started SebselectDemoApplication in 4.22 seconds (process running for 4.669) Hibernate: select next value for t_customer_seq Hibernate: insert into t_customer (email_address,name,type,id) values (?,?,?,?) Hibernate: select next value for t_order_seq Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: select next value for t_order_seq Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: select next value for t_customer_seq Hibernate: insert into t_customer (email_address,name,type,id) values (?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_customer (email_address,name,type,id) values (?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_customer (email_address,name,type,id) values (?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_customer (email_address,name,type,id) values (?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: insert into t_order (amount,cust_id,name,quantity,id) values (?,?,?,?,?) Hibernate: select o1_0.id,o1_0.amount,o1_0.cust_id,o1_0.name,o1_0.quantity from t_order o1_0 Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Order [customer=Customer [emailAddress=mary@test.com, id=1, name=mary, type=null], id=1, name=XBox, quantity=4, amount=2400.00] Order [customer=Customer [emailAddress=mary@test.com, id=1, name=mary, type=null], id=2, name=PC, quantity=0, amount=1000.00] Order [customer=Customer [emailAddress=zheng@test.com, id=2, name=zheng, type=null], id=3, name=TV, quantity=3, amount=9000.00] Order [customer=Customer [emailAddress=zheng@test.com, id=2, name=zheng, type=null], id=4, name=PC, quantity=0, amount=1000.00] Order [customer=Customer [emailAddress=john@test.com, id=3, name=john, type=null], id=5, name=PC, quantity=0, amount=1000.00] Order [customer=Customer [emailAddress=john@test.com, id=3, name=john, type=null], id=6, name=Phone, quantity=1, amount=500.00] Order [customer=Customer [emailAddress=bob@test.com, id=4, name=bob, type=null], id=7, name=PC, quantity=0, amount=1000.00] Order [customer=Customer [emailAddress=bob@test.com, id=4, name=bob, type=null], id=8, name=PC, quantity=0, amount=1000.00] Order [customer=Customer [emailAddress=terry@test.com, id=5, name=terry, type=null], id=9, name=XBox, quantity=4, amount=2400.00] Order [customer=Customer [emailAddress=terry@test.com, id=5, name=terry, type=null], id=10, name=PC, quantity=0, amount=1000.00] Hibernate: select co1_0.id,co1_0.name,co1_0.total from ( select t_customer.id id, t_customer.name name, a.total total from t_customer ,( select c.id, sum(o.amount) total from t_customer c, t_order o where c.id = o.cust_id group by c.id ) a where a.id = t_customer.id ) co1_0 Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Hibernate: select c1_0.id,c1_0.email_address,c1_0.name,c1_0.type from t_customer c1_0 where c1_0.id=? Hibernate: update t_customer set email_address=?,name=?,type=? where id=? Major customers with total exceed 5000. Hibernate: select mc1_0.id,mc1_0.email,mc1_0.name from ( SELECT id, name, email_Address as email FROM t_customer where type= 'Major' ) mc1_0 zheng
- Line 28-32: H2 table
T_Customer,T_Orderare created. - Line 62-71: Customer order details.
- Line 76, 78: print out the major customers.
Open a web browser and navigate to the http://localhost:8080/customerOrders. You should see the following response:
Customer Orders
[
{
"id": 1,
"customerName": "mary",
"totalOrderAmount": 3400
},
{
"id": 2,
"customerName": "zheng",
"totalOrderAmount": 10000
},
{
"id": 3,
"customerName": "john",
"totalOrderAmount": 1500
},
{
"id": 4,
"customerName": "bob",
"totalOrderAmount": 2000
},
{
"id": 5,
"customerName": "terry",
"totalOrderAmount": 3400
}
]As you see, the totalOrderAmount for customer zheng exceeds 5000. Navigate to the http://localhost:8080/majorCustomers. You should see the following response:
Major Customers
[
{
"email": "zheng@test.com",
"id": 2,
"name": "zheng"
}
]7. Conclusion
@Subselect is Hibernate featurue and not part of the JPA specification, so it may not be supported by JPA providers. Red Hat(Jboss) and Spring framework support @Subselect, but EclipseLink, OpenJPA, and DataNucleus don’t support @Subselect. It offers significant benefits for creating read-only views of data but comes with following constraints:
- Lack of caching: the results are not cached due to complex queries. This can impact performance if the subquery is expensive.
- Database dependence: The subquery is written in SQL, making it database-dependent.
- Initialization order: The underlying tables referenced in the subquery must be created before the
@Subselectentity is accessed. This can lead to initialization issues in some cases. - Data consistency: since
@Subselectentities are read-only, any changes in the underlying tables are not immediately reflected in these entities unless the session is refreshed or the query is re-executed. - Maintenance: maintaining complex subqueries within the entity definition can become challenging, especially as the database schema evolves.
- Performance trade-offs: while
@Subselectcan optimize read-heavy operations, it can also introduce performance bottlenecks if the subquery is not well-optimized.
8. Download
This was an example of Spring Boot web application which maps an immutable entity with @Subselect annotation.
You can download the full source code of this example here: Hibernate @Subselect Annotation





