From 8ddac32f4ae666e1f888e81d8c13f28922ac9cad Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 20 Sep 2024 19:10:17 +0200 Subject: [PATCH] introduce ManagedEntityMode finally addresses issue #62 Signed-off-by: Gavin King --- .../jakarta/persistence/EntityManager.java | 21 ++++++++++ .../persistence/ManagedEntityMode.java | 41 +++++++++++++++++++ .../jakarta/persistence/NamedNativeQuery.java | 7 ++++ .../java/jakarta/persistence/NamedQuery.java | 9 +++- .../main/java/jakarta/persistence/Query.java | 16 ++++++++ .../java/jakarta/persistence/TypedQuery.java | 8 ++++ .../main/asciidoc/ch03-entity-operations.adoc | 27 +++++++++++- 7 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 api/src/main/java/jakarta/persistence/ManagedEntityMode.java diff --git a/api/src/main/java/jakarta/persistence/EntityManager.java b/api/src/main/java/jakarta/persistence/EntityManager.java index 80b41a19..119521bc 100644 --- a/api/src/main/java/jakarta/persistence/EntityManager.java +++ b/api/src/main/java/jakarta/persistence/EntityManager.java @@ -892,6 +892,27 @@ void refresh(Object entity, */ void clear(); + /** + * Obtain the {@link ManagedEntityMode} of the given entity. + * @param entity a persistent entity associated with the + * persistence context + * @throws IllegalArgumentException if the instance is not an entity + * or if the entity is not managed + * @since 4.0 + */ + ManagedEntityMode getManagedEntityMode(Object entity); + + /** + * Set the {@link ManagedEntityMode} of the given entity. + * @param entity a persistent entity associated with the + * persistence context + * @param mode the new mode + * @throws IllegalArgumentException if the instance is not an entity + * or if the entity is not managed + * @since 4.0 + */ + void setManagedEntityMode(Object entity, ManagedEntityMode mode); + /** * Evict the given managed or removed entity from the persistence * context, causing the entity to become immediately detached. diff --git a/api/src/main/java/jakarta/persistence/ManagedEntityMode.java b/api/src/main/java/jakarta/persistence/ManagedEntityMode.java new file mode 100644 index 00000000..c5efb284 --- /dev/null +++ b/api/src/main/java/jakarta/persistence/ManagedEntityMode.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008, 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Gavin King - 4.0 + +package jakarta.persistence; + +/** + * A {@link FindOption} which specifies whether entities are + * to be loaded in {@linkplain #READ_ONLY read only} mode or + * in regular {@linkplain #READ_WRITE modifiable} mode. + * + * @since 4.0 + */ +public enum ManagedEntityMode implements FindOption { + /** + * Specifies that an entity should be loaded in read-only mode. + *

+ * Read-only entities can be modified, but changes are not + * synchronized with the database. + */ + READ_ONLY, + /** + * Specifies that an entity should be loaded in the default + * modifiable mode. + *

+ * Changes made to modifiable entities are synchronized to + * with the database when the persistence context is flushed. + */ + READ_WRITE +} diff --git a/api/src/main/java/jakarta/persistence/NamedNativeQuery.java b/api/src/main/java/jakarta/persistence/NamedNativeQuery.java index 14bb9073..a9841a62 100644 --- a/api/src/main/java/jakarta/persistence/NamedNativeQuery.java +++ b/api/src/main/java/jakarta/persistence/NamedNativeQuery.java @@ -95,6 +95,13 @@ */ String query(); + /** + * (Optional) The {@link ManagedEntityMode} to use for entities + * loaded during execution of the query. + * @since 4.0 + */ + ManagedEntityMode managedEntityMode() default ManagedEntityMode.READ_WRITE; + /** * Query properties and hints. * (May include vendor-specific query hints.) diff --git a/api/src/main/java/jakarta/persistence/NamedQuery.java b/api/src/main/java/jakarta/persistence/NamedQuery.java index e0b88ea2..54544339 100644 --- a/api/src/main/java/jakarta/persistence/NamedQuery.java +++ b/api/src/main/java/jakarta/persistence/NamedQuery.java @@ -88,7 +88,14 @@ * @since 2.0 */ LockModeType lockMode() default LockModeType.NONE; - + + /** + * (Optional) The {@link ManagedEntityMode} to use for entities + * loaded during execution of the query. + * @since 4.0 + */ + ManagedEntityMode managedEntityMode() default ManagedEntityMode.READ_WRITE; + /** * (Optional) Query properties and hints. May include * vendor-specific query hints. diff --git a/api/src/main/java/jakarta/persistence/Query.java b/api/src/main/java/jakarta/persistence/Query.java index a031eb75..9b40e19c 100644 --- a/api/src/main/java/jakarta/persistence/Query.java +++ b/api/src/main/java/jakarta/persistence/Query.java @@ -576,6 +576,22 @@ Query setParameter(int position, Date value, */ Integer getTimeout(); + /** + * Set the {@link ManagedEntityMode} to be used for entities + * loaded during execution of this query. + * + * @since 4.0 + */ + Query setManagedEntityMode(ManagedEntityMode managedEntityMode); + + /** + * The {@link ManagedEntityMode} that will be in effect during + * execution of this query. + * + * @since 4.0 + */ + ManagedEntityMode getManagedEntityMode(); + /** * Return an object of the specified type to allow access to * a provider-specific API. If the provider implementation of diff --git a/api/src/main/java/jakarta/persistence/TypedQuery.java b/api/src/main/java/jakarta/persistence/TypedQuery.java index 9fd6edc9..ee104c27 100644 --- a/api/src/main/java/jakarta/persistence/TypedQuery.java +++ b/api/src/main/java/jakarta/persistence/TypedQuery.java @@ -357,4 +357,12 @@ TypedQuery setParameter(int position, Date value, * @since 3.2 */ TypedQuery setTimeout(Integer timeout); + + /** + * Set the {@link ManagedEntityMode} to be used for entities + * loaded during execution of this query. + * + * @since 4.0 + */ + TypedQuery setManagedEntityMode(ManagedEntityMode managedEntityMode); } diff --git a/spec/src/main/asciidoc/ch03-entity-operations.adoc b/spec/src/main/asciidoc/ch03-entity-operations.adoc index 830e397b..55fff3da 100644 --- a/spec/src/main/asciidoc/ch03-entity-operations.adoc +++ b/spec/src/main/asciidoc/ch03-entity-operations.adoc @@ -234,6 +234,26 @@ After an entity has been removed, its state (except for generated state) will be that of the entity at the point at which the remove operation was called. +==== Read-only entities + +The enumeration `ManagedEntityMode` allows an entity loaded into the +persistence context via a call to `find()` or via execution of a query +to be loaded in one of two modes. + +[source,java] +---- +include::../../../../api/src/main/java/jakarta/persistence/ManagedEntityMode.java[lines=18..-1] +---- + +By default, an entity is loaded in modifiable mode, and its state is +synchronized to the database when a flush operation occurs. An entity +may be loaded in read-only mode by passing `READ_ONLY` to +`EntityManager.find()` or `Query.setManagedEntityMode()`. Furthermore, +the current mode for a given managed entity may be changed by calling +`EntityManager.setManagedEntityMode()`. Changes made to an entity instance +currently loaded in read-only mode are not synchronized to the database, +and do not become persistent. + ==== Synchronization to the Database [[a1955]] In general, a persistence context will be @@ -299,8 +319,8 @@ transaction, the persistence provider must not flush to the database. The semantics of the flush operation, applied to an entity X are as follows: -* If X is a managed entity, it is synchronized -to the database. +* If X is a managed entity, and it was loaded with +`ManagedEntityMode.READ_WRITE`, it is synchronized to the database. ** For all entities Y referenced by a relationship from X, if the relationship to Y has been annotated with the `cascade` element value `cascade=PERSIST` or `cascade=ALL`, the @@ -315,6 +335,9 @@ transaction marked for rollback) or the transaction commit will fail. the ownership of the relationship. If X owns the relationship, any changes to the relationship are synchronized with the database; otherwise, if Y owns the relationships, the behavior is undefined. +* If X is a managed entity, and it was loaded with +`ManagedEntityMode.READ_ONLY`, it is ignored by the flush operation, +and is not synchronized with the database. * If X is a removed entity, it is removed from the database. No `cascade` options are relevant.