Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@
* <li>the foreign key, in the case of a collection.
* </ol>
* <p>
* If the {@linkplain #querySpaces tables which affect the query results}
* are specified, then changes to those tables will be flushed before
* execution of the query.
* <p>
* For example:
* <pre>
* &#64;SQLSelect(sql = """
* SELECT id, created, text
* FROM records
* WHERE id = ? and deleted is null
* """
* querySpaces = "records")
* &#64;Entity
* public class Record { ... }
* </pre>
* <p>
* Optionally, an explicit {@linkplain #resultSetMapping result set mapping}
* may be specified. It should have:
* <ol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,49 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Maps an immutable and read-only entity to a given SQL {@code select} expression.
* Maps an {@linkplain Immutable immutable} and read-only entity to a given
* SQL {@code select} expression.
* <p>
* This is an alternative to defining a database view and mapping the entity to
* the view using the {@link jakarta.persistence.Table @Table} annotation.
* For example:
* <pre>
* &#64;Immutable &#64;Entity
* &#64;Subselect("""
* select type, sum(amount) as total, avg(amount) as average
* from details
* group by type
* """)
* &#64;Synchronize("details")
* public class Summary {
* &#64;Id String type;
* Double total;
* Double average;
* }
* </pre>
* <p>
* This is an alternative to defining a {@linkplain View view} and mapping
* the entity to the view using the {@link jakarta.persistence.Table @Table}
* annotation.
* <p>
* It's possible to have an entity class which maps a table, and another
* entity which is defined by a {@code @Subselect} involving the same table.
* In this case, a stateful session is vulnerable to data aliasing effects,
* and it's the responsibility of client code to ensure that changes to the
* first entity are flushed to the database before reading the same data via
* the second entity. The {@link Synchronize @Synchronize} annotation can
* help alleviate this problem, but it's an incomplete solution. We therefore
* recommend the use of {@linkplain org.hibernate.StatelessSession stateless
* sessions} in this situation.
*
* @see Synchronize
* @see View
*
* @author Sharath Reddy
*/
@Target(TYPE)
@Retention(RUNTIME)
public @interface Subselect {
/**
* The query.
* The subquery, written in native SQL.
*/
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* Ordinarily, Hibernate knows the tables containing the state of an
* entity or collection. This annotation might be necessary if:
* <ul>
* <li>an entity or collection maps a database view,
* <li>an entity or collection maps a database {@linkplain View view},
* <li>an entity or collection is persisted using handwritten SQL,
* that is, using {@link SQLSelect @SQLSelect} and friends, or
* <li>an entity is mapped using {@link Subselect @Subselect}.
Expand All @@ -41,6 +41,9 @@
* @author Sharath Reddy
*
* @see org.hibernate.query.SynchronizeableQuery
* @see View
* @see Subselect
* @see SQLSelect
*/
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
Expand Down
15 changes: 11 additions & 4 deletions hibernate-core/src/main/java/org/hibernate/annotations/View.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,24 @@
* <pre>
* &#64;Immutable &#64;Entity
* &#64;Table(name="summary")
* &#64;View(query="select type, sum(amount) as total, avg(amount) as average from details group by type")
* &#64;View(query="""
* select type, sum(amount) as total, avg(amount) as average
* from details
* group by type
* """)
* &#64;Synchronize("details")
* public class Summary {
* &#64;Id String type;
* Double total;
* Double average;
* }
* </pre>
* <p>
* results in the following generated DDL:
* <pre>
* create view summary
* as select type, sum(amount) as total, avg(amount) as average from details group by type
* as select type, sum(amount) as total, avg(amount) as average
* from details
* group by type
* </pre>
* <p>
* If a view is not updatable, we recommend annotating the
Expand All @@ -47,7 +52,7 @@
* It's possible to have an entity class which maps a table,
* and another entity which maps a view defined as a query
* against that table. In this case, a stateful session is
* vulnerable to data aliasing effects, and it is the
* vulnerable to data aliasing effects, and it's the
* responsibility of client code to ensure that changes to
* the first entity are flushed to the database before
* reading the same data via the second entity. The
Expand All @@ -61,6 +66,8 @@
* @since 6.3
*
* @author Gavin King
*
* @see Synchronize
*/
@Incubating
@Target(TYPE)
Expand Down
Loading