Author: drobiazko Date: Wed Mar 9 23:00:50 2011 New Revision: 1080050 URL: http://svn.apache.org/viewvc?rev=1080050&view=rev Log: TAP5-1472: Added ApplicationStatePersistenceStrategy for JPA entities.
Added: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityApplicationStatePersistenceStrategy.java (with props) tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/JpaInternalUtils.java (with props) tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaPersistenceConstants.java (with props) tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/example/app/pages/SSOEntity.java (with props) tapestry/tapestry5/trunk/tapestry-jpa/src/test/webapp/SSOEntity.tml Modified: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityPersistentFieldStrategy.java tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaModule.java tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaSymbols.java tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/integration/JpaIntegrationTest.java Added: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityApplicationStatePersistenceStrategy.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityApplicationStatePersistenceStrategy.java?rev=1080050&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityApplicationStatePersistenceStrategy.java (added) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityApplicationStatePersistenceStrategy.java Wed Mar 9 23:00:50 2011 @@ -0,0 +1,86 @@ +// Copyright 2011 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.jpa; + +import org.apache.tapestry5.internal.services.SessionApplicationStatePersistenceStrategy; +import org.apache.tapestry5.jpa.EntityManagerManager; +import org.apache.tapestry5.services.ApplicationStateCreator; +import org.apache.tapestry5.services.Request; + +public class EntityApplicationStatePersistenceStrategy extends + SessionApplicationStatePersistenceStrategy +{ + private final EntityManagerManager entityManagerManager; + + public EntityApplicationStatePersistenceStrategy(final Request request, + final EntityManagerManager entityManagerManager) + { + super(request); + + this.entityManagerManager = entityManagerManager; + } + + @Override + public <T> T get(final Class<T> ssoClass, final ApplicationStateCreator<T> creator) + { + final Object persistedValue = getOrCreate(ssoClass, creator); + + if (persistedValue instanceof PersistedEntity) + { + final PersistedEntity persisted = (PersistedEntity) persistedValue; + + final Object restored = persisted.restore(entityManagerManager); + + // shall we maybe throw an exception instead? + if (restored == null) + { + set(ssoClass, null); + return (T) getOrCreate(ssoClass, creator); + } + + return (T) restored; + } + + return (T) persistedValue; + } + + @Override + public <T> void set(final Class<T> ssoClass, final T sso) + { + final String key = buildKey(ssoClass); + Object entity; + + if (sso != null) + { + try + { + entity = JpaInternalUtils.convertApplicationValueToPersisted(entityManagerManager, + sso); + } + catch (final RuntimeException ex) + { + // if entity not attached to an EntityManager yet, store it as usual sso + entity = sso; + } + } + else + { + entity = sso; + } + + getSession().setAttribute(key, entity); + } + +} Propchange: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityApplicationStatePersistenceStrategy.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityPersistentFieldStrategy.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityPersistentFieldStrategy.java?rev=1080050&r1=1080049&r2=1080050&view=diff ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityPersistentFieldStrategy.java (original) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/EntityPersistentFieldStrategy.java Wed Mar 9 23:00:50 2011 @@ -14,17 +14,8 @@ package org.apache.tapestry5.internal.jpa; -import java.util.Map; -import java.util.Set; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.Metamodel; - import org.apache.tapestry5.internal.services.AbstractSessionPersistentFieldStrategy; import org.apache.tapestry5.jpa.EntityManagerManager; -import org.apache.tapestry5.jpa.JpaConstants; import org.apache.tapestry5.services.Request; /** @@ -45,18 +36,7 @@ public class EntityPersistentFieldStrate @Override protected Object convertApplicationValueToPersisted(final Object newValue) { - final EntityManager em = getEntityManagerFactory(newValue); - - final EntityManagerFactory emf = em.getEntityManagerFactory(); - - final Map<String, Object> properties = emf.getProperties(); - - final String persistenceUnitName = (String) properties - .get(JpaConstants.PERSISTENCE_UNIT_NAME); - - final Object id = emf.getPersistenceUnitUtil().getIdentifier(newValue); - - return new PersistedEntity(newValue.getClass(), id, persistenceUnitName); + return JpaInternalUtils.convertApplicationValueToPersisted(entityManagerManager, newValue); } @Override @@ -66,31 +46,4 @@ public class EntityPersistentFieldStrate return persisted.restore(entityManagerManager); } - - private EntityManager getEntityManagerFactory(final Object entity) - { - final Map<String, EntityManager> entityManagers = entityManagerManager.getEntityManagers(); - - for (final EntityManager em : entityManagers.values()) - { - final EntityManagerFactory emf = em.getEntityManagerFactory(); - - final Metamodel metamodel = emf.getMetamodel(); - - final Set<EntityType<?>> entities = metamodel.getEntities(); - - for (final EntityType<?> entityType : entities) - { - if (entityType.getJavaType() == entity.getClass()) - { - if (em.contains(entity)) { return em; } - } - } - } - - throw new IllegalArgumentException( - String.format( - "Failed persisting an entity in the session. The entity '%s' does not belong to any of the existing persistence contexts.", - entity)); - } } Added: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/JpaInternalUtils.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/JpaInternalUtils.java?rev=1080050&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/JpaInternalUtils.java (added) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/JpaInternalUtils.java Wed Mar 9 23:00:50 2011 @@ -0,0 +1,74 @@ +// Copyright 2011 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.jpa; + +import java.util.Map; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.metamodel.EntityType; +import javax.persistence.metamodel.Metamodel; + +import org.apache.tapestry5.jpa.EntityManagerManager; +import org.apache.tapestry5.jpa.JpaConstants; + +public class JpaInternalUtils +{ + public static PersistedEntity convertApplicationValueToPersisted( + final EntityManagerManager entityManagerManager, final Object newValue) + { + final EntityManager em = getEntityManagerFactory(entityManagerManager, newValue); + + final EntityManagerFactory emf = em.getEntityManagerFactory(); + + final Map<String, Object> properties = emf.getProperties(); + + final String persistenceUnitName = (String) properties + .get(JpaConstants.PERSISTENCE_UNIT_NAME); + + final Object id = emf.getPersistenceUnitUtil().getIdentifier(newValue); + + return new PersistedEntity(newValue.getClass(), id, persistenceUnitName); + } + + private static EntityManager getEntityManagerFactory( + final EntityManagerManager entityManagerManager, final Object entity) + { + final Map<String, EntityManager> entityManagers = entityManagerManager.getEntityManagers(); + + for (final EntityManager em : entityManagers.values()) + { + final EntityManagerFactory emf = em.getEntityManagerFactory(); + + final Metamodel metamodel = emf.getMetamodel(); + + final Set<EntityType<?>> entities = metamodel.getEntities(); + + for (final EntityType<?> entityType : entities) + { + if (entityType.getJavaType() == entity.getClass()) + { + if (em.contains(entity)) { return em; } + } + } + } + + throw new IllegalArgumentException( + String.format( + "Failed persisting an entity in the session. The entity '%s' does not belong to any of the existing persistence contexts.", + entity)); + } +} Propchange: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/internal/jpa/JpaInternalUtils.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaModule.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaModule.java?rev=1080050&r1=1080049&r2=1080050&view=diff ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaModule.java (original) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaModule.java Wed Mar 9 23:00:50 2011 @@ -23,6 +23,7 @@ import javax.persistence.spi.Persistence import org.apache.tapestry5.ValueEncoder; import org.apache.tapestry5.internal.jpa.CommitAfterWorker; +import org.apache.tapestry5.internal.jpa.EntityApplicationStatePersistenceStrategy; import org.apache.tapestry5.internal.jpa.EntityManagerManagerImpl; import org.apache.tapestry5.internal.jpa.EntityManagerObjectProvider; import org.apache.tapestry5.internal.jpa.EntityManagerSourceImpl; @@ -46,6 +47,10 @@ import org.apache.tapestry5.ioc.services import org.apache.tapestry5.ioc.services.RegistryShutdownHub; import org.apache.tapestry5.ioc.services.SymbolProvider; import org.apache.tapestry5.ioc.services.TypeCoercer; +import org.apache.tapestry5.services.ApplicationStateContribution; +import org.apache.tapestry5.services.ApplicationStateManager; +import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy; +import org.apache.tapestry5.services.ApplicationStatePersistenceStrategySource; import org.apache.tapestry5.services.ComponentClassTransformWorker; import org.apache.tapestry5.services.PersistentFieldStrategy; import org.apache.tapestry5.services.ValueEncoderFactory; @@ -82,7 +87,16 @@ public class JpaModule public static void provideEntityPersistentFieldStrategies( final MappedConfiguration<String, PersistentFieldStrategy> configuration) { - configuration.addInstance("entity", EntityPersistentFieldStrategy.class); + configuration.addInstance(JpaPersistenceConstants.ENTITY, + EntityPersistentFieldStrategy.class); + } + + @Contribute(ApplicationStatePersistenceStrategySource.class) + public void provideApplicationStatePersistenceStrategies( + final MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration) + { + configuration.addInstance(JpaPersistenceConstants.ENTITY, + EntityApplicationStatePersistenceStrategy.class); } @Contribute(ComponentClassTransformWorker.class) @@ -115,6 +129,7 @@ public class JpaModule { configuration.add(JpaSymbols.PROVIDE_ENTITY_VALUE_ENCODERS, "true"); configuration.add(JpaSymbols.EARLY_START_UP, "true"); + configuration.add(JpaSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED, "true"); } @Contribute(ValueEncoderSource.class) @@ -157,6 +172,34 @@ public class JpaModule } } + @Contribute(ApplicationStateManager.class) + public static void provideApplicationStateContributions( + final MappedConfiguration<Class, ApplicationStateContribution> configuration, + final EntityManagerSource entityManagerSource, + @Symbol(JpaSymbols.PROVIDE_ENTITY_VALUE_ENCODERS) + final boolean entitySessionStatePersistenceStrategyEnabled) + { + + if (!entitySessionStatePersistenceStrategyEnabled) + return; + + for (final PersistenceUnitInfo info : entityManagerSource.getPersistenceUnitInfos()) + { + final EntityManagerFactory emf = entityManagerSource.getEntityManagerFactory(info + .getPersistenceUnitName()); + + for (final String className : info.getManagedClassNames()) + { + final Metamodel metamodel = emf.getMetamodel(); + + final Class<?> clazz = loadClass(info, className); + + configuration.add(clazz, new ApplicationStateContribution( + JpaPersistenceConstants.ENTITY)); + } + } + } + @Startup public static void startupEarly(final EntityManagerSource entityManagerSource, @Symbol(JpaSymbols.EARLY_START_UP) Added: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaPersistenceConstants.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaPersistenceConstants.java?rev=1080050&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaPersistenceConstants.java (added) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaPersistenceConstants.java Wed Mar 9 23:00:50 2011 @@ -0,0 +1,27 @@ +// Copyright 2011 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.jpa; + +import org.apache.tapestry5.PersistenceConstants; + +public class JpaPersistenceConstants +{ + /** + * If the field's value is a persistent JPA entity, its type and primary key is stored in the + * {@link org.apache.tapestry5.services.Session}. Otherwise, + * the value is stored as per {@link PersistenceConstants#SESSION}. + */ + public static final String ENTITY = "entity"; +} Propchange: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaPersistenceConstants.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaSymbols.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaSymbols.java?rev=1080050&r1=1080049&r2=1080050&view=diff ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaSymbols.java (original) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/main/java/org/apache/tapestry5/jpa/JpaSymbols.java Wed Mar 9 23:00:50 2011 @@ -20,6 +20,16 @@ public class JpaSymbols /** * If "true", then JPA will be started up at application launch, rather than lazily. + * + * @since 5.3.0 */ public static final String EARLY_START_UP = "tapestry.jpa.early-startup"; + + /** + * If true, then "entity" persistence strategy is used to store JPA entities as + * {@code Session State Objects}. + * + * @since 5.3.0 + */ + public static final String ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED = "tapestry.jpa.entity-session-state-persistence-strategy-enabled"; } Modified: tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/integration/JpaIntegrationTest.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/integration/JpaIntegrationTest.java?rev=1080050&r1=1080049&r2=1080050&view=diff ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/integration/JpaIntegrationTest.java (original) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/integration/JpaIntegrationTest.java Wed Mar 9 23:00:50 2011 @@ -14,7 +14,9 @@ package org.apache.tapestry5.jpa.integration; +import org.apache.tapestry5.internal.jpa.PersistedEntity; import org.apache.tapestry5.test.SeleniumTestCase; +import org.example.app.entities.User; import org.testng.annotations.Test; public class JpaIntegrationTest extends SeleniumTestCase @@ -62,4 +64,36 @@ public class JpaIntegrationTest extends clickAndWait("link=set to transient"); assertTextPresent("Failed persisting an entity in the session."); } + + @Test + public void sso_entities() + { + open("/ssoentity"); + assertEquals(getText("//span[@id='name']").length(), 0); + assertText("//span[@id='persistedEntityClassName']", User.class.getName()); + + clickAndWait("link=persist entity"); + assertText("//span[@id='name']", "name"); + assertText("//span[@id='persistedEntityClassName']", PersistedEntity.class.getName()); + + // can set back to null + clickAndWait("link=set to null"); + assertEquals(getText("//span[@id='name']").length(), 0); + assertText("//span[@id='persistedEntityClassName']", User.class.getName()); + + clickAndWait("link=persist entity"); + assertText("//span[@id='name']", "name"); + assertText("//span[@id='persistedEntityClassName']", PersistedEntity.class.getName()); + + clickAndWait("link=delete"); + assertEquals(getText("//span[@id='name']").length(), 0); + assertText("//span[@id='persistedEntityClassName']", User.class.getName()); + + clickAndWait("link=persist entity"); + assertText("//span[@id='name']", "name"); + assertText("//span[@id='persistedEntityClassName']", PersistedEntity.class.getName()); + + clickAndWait("link=set to transient"); + assertText("//span[@id='persistedEntityClassName']", User.class.getName()); + } } Added: tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/example/app/pages/SSOEntity.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/example/app/pages/SSOEntity.java?rev=1080050&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/example/app/pages/SSOEntity.java (added) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/example/app/pages/SSOEntity.java Wed Mar 9 23:00:50 2011 @@ -0,0 +1,81 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.example.app.pages; + +import java.util.List; + +import javax.persistence.PersistenceUnit; + +import org.apache.tapestry5.annotations.Property; +import org.apache.tapestry5.annotations.SessionState; +import org.apache.tapestry5.ioc.annotations.Inject; +import org.apache.tapestry5.jpa.CommitAfter; +import org.apache.tapestry5.services.Request; +import org.apache.tapestry5.services.Session; +import org.example.app.AppConstants; +import org.example.app.entities.User; +import org.example.app.services.UserDAO; + +public class SSOEntity +{ + @SessionState + @Property + private User user; + + @Inject + private UserDAO userDAO; + + @Inject + private Request request; + + @CommitAfter + @PersistenceUnit(unitName = AppConstants.TEST_PERSISTENCE_UNIT) + void onPersistEntity() + { + final User user = new User(); + user.setFirstName("name"); + + userDAO.add(user); + + this.user = user; + } + + void onSetToNull() + { + user = null; + } + + void onSetToTransient() + { + user = new User(); + } + + @CommitAfter + @PersistenceUnit(unitName = AppConstants.TEST_PERSISTENCE_UNIT) + void onDelete() + { + final List<User> users = userDAO.findAll(); + + userDAO.delete(users.toArray(new User[0])); + } + + public String getPersistedEntityClassName() + { + final Session session = request.getSession(true); + + final Object value = session.getAttribute("sso:" + User.class.getName()); + + return value.getClass().getName(); + } +} Propchange: tapestry/tapestry5/trunk/tapestry-jpa/src/test/java/org/example/app/pages/SSOEntity.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: tapestry/tapestry5/trunk/tapestry-jpa/src/test/webapp/SSOEntity.tml URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-jpa/src/test/webapp/SSOEntity.tml?rev=1080050&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-jpa/src/test/webapp/SSOEntity.tml (added) +++ tapestry/tapestry5/trunk/tapestry-jpa/src/test/webapp/SSOEntity.tml Wed Mar 9 23:00:50 2011 @@ -0,0 +1,10 @@ +<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"> +<body> + <p>entity name: <span id="name">${user?.firstName}</span></p> + <p>persisted entity class name: <span id="persistedEntityClassName">${persistedEntityClassName}</span></p> + <p><t:eventlink event="persistEntity">persist entity</t:eventlink></p> + <p><t:eventlink event="setToNull">set to null</t:eventlink></p> + <p><t:eventlink event="setToTransient">set to transient</t:eventlink></p> + <p><t:eventlink event="delete">delete</t:eventlink></p> +</body> +</html> \ No newline at end of file