You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by da...@apache.org on 2008/03/09 02:30:21 UTC
svn commit: r635133 - in /tapestry/tapestry5/trunk/tapestry-hibernate/src:
main/java/org/apache/tapestry/hibernate/
main/java/org/apache/tapestry/internal/hibernate/
main/resources/org/apache/tapestry/internal/hibernate/ site/apt/
test/java/org/apache/...
Author: dadams
Date: Sat Mar 8 17:30:20 2008
New Revision: 635133
URL: http://svn.apache.org/viewvc?rev=635133&view=rev
Log:
TAPESTRY-2246: Add @Persist strategy for Hibernate entities
Added:
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategy.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategyTest.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/example/app0/pages/PersistEntity.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/test/webapp/PersistEntity.tml
Modified:
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateModule.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateSessionManager.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/HibernateMessages.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/resources/org/apache/tapestry/internal/hibernate/HibernateStrings.properties
tapestry/tapestry5/trunk/tapestry-hibernate/src/site/apt/userguide.apt
tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/hibernate/integration/TapestryHibernateIntegrationTests.java
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateModule.java?rev=635133&r1=635132&r2=635133&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateModule.java Sat Mar 8 17:30:20 2008
@@ -17,6 +17,7 @@
import org.apache.tapestry.ValueEncoder;
import org.apache.tapestry.internal.InternalConstants;
import org.apache.tapestry.internal.hibernate.DefaultHibernateConfigurer;
+import org.apache.tapestry.internal.hibernate.EntityPersistentFieldStrategy;
import org.apache.tapestry.internal.hibernate.HibernateEntityValueEncoder;
import org.apache.tapestry.internal.hibernate.HibernateSessionManagerImpl;
import org.apache.tapestry.internal.hibernate.HibernateSessionSourceImpl;
@@ -37,6 +38,7 @@
import org.apache.tapestry.ioc.services.RegistryShutdownHub;
import org.apache.tapestry.ioc.services.TypeCoercer;
import org.apache.tapestry.services.AliasContribution;
+import org.apache.tapestry.services.PersistentFieldStrategy;
import org.apache.tapestry.services.ValueEncoderFactory;
import org.hibernate.Session;
import org.hibernate.mapping.PersistentClass;
@@ -164,4 +166,13 @@
configuration.add(entityClass, factory);
}
}
+
+ /**
+ * Contributes the following: <dl> <dt>entity</dt> <dd>Stores the id of the entity and reloads from the {@link Session}</dd> </dl>
+ */
+ public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration, ObjectLocator locator)
+ {
+ configuration.add("entity", locator.autobuild(EntityPersistentFieldStrategy.class));
+ }
+
}
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateSessionManager.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateSessionManager.java?rev=635133&r1=635132&r2=635133&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateSessionManager.java (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/hibernate/HibernateSessionManager.java Sat Mar 8 17:30:20 2008
@@ -21,10 +21,10 @@
* needed, allowing the session to checkpoint (commit the current transaction and continue) and
* commit the transaction automatically at the end of the request.
* <p>
- * Remember that in Tapestry, action requests and render requests are entirely seperate, and you
- * will see a seperate request and a seperate transaction for each. Care should be taken to ensure
+ * Remember that in Tapestry, action requests and render requests are entirely separate, and you
+ * will see a separate request and a separate transaction for each. Care should be taken to ensure
* that entity objects that are retained (in the session, as persistent field values) between
- * requests are handled correct (they tend to become detached instances).
+ * requests are handled correctly (they tend to become detached instances).
* <p>
* This implementation of this service is per-thread.
*/
@@ -46,7 +46,7 @@
void commit();
/**
- * Aborts the current transaction, and starts a new tranasction to replace it.
+ * Aborts the current transaction, and starts a new transaction to replace it.
*/
void abort();
}
Added: tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategy.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategy.java?rev=635133&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategy.java (added)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategy.java Sat Mar 8 17:30:20 2008
@@ -0,0 +1,111 @@
+// Copyright 2006, 2007, 2008 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.tapestry.internal.hibernate;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.tapestry.internal.services.AbstractSessionPersistentFieldStrategy;
+import org.apache.tapestry.internal.services.PersistentFieldChangeImpl;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry.ioc.internal.util.Defense;
+import org.apache.tapestry.ioc.services.TypeCoercer;
+import org.apache.tapestry.services.PersistentFieldChange;
+import org.apache.tapestry.services.PersistentFieldStrategy;
+import org.apache.tapestry.services.Request;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.metadata.ClassMetadata;
+
+/** Persists Hibernate entities by storing their id in the session. */
+public class EntityPersistentFieldStrategy implements PersistentFieldStrategy {
+ private static final Pattern KEY_PATTERN = Pattern.compile("^([^:]+):([^:]+):(.+)$");
+
+ private final PersistentFieldStrategy _strategy;
+ private final Session _session;
+ private final TypeCoercer _typeCoercer;
+
+ public EntityPersistentFieldStrategy(Session session, TypeCoercer typeCoercer, Request request) {
+ super();
+ _strategy = new EntityStrategy(request);
+ _session = session;
+ _typeCoercer = typeCoercer;
+ }
+
+ public void discardChanges(String pageName) {
+ _strategy.discardChanges(pageName);
+ }
+
+ public Collection<PersistentFieldChange> gatherFieldChanges(String pageName) {
+ Collection<PersistentFieldChange> changes = CollectionFactory.newList();
+ for(PersistentFieldChange change : _strategy.gatherFieldChanges(pageName)) {
+ if (change.getValue() == null) {
+ changes.add(change);
+ continue;
+ }
+
+ String key = change.getValue().toString();
+ Matcher matcher = KEY_PATTERN.matcher(key);
+ matcher.matches();
+
+ String entityName = matcher.group(1);
+ String idClassName = matcher.group(2);
+ String stringId = matcher.group(3);
+
+ try {
+ Class<?> idClass = Class.forName(idClassName);
+ Object idObj = _typeCoercer.coerce(stringId, idClass);
+
+ Serializable id = Defense.cast(idObj, Serializable.class, "id");
+ Object entity = _session.get(entityName, id);
+ changes.add(new PersistentFieldChangeImpl(change.getComponentId(), change.getFieldName(), entity));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(HibernateMessages.badEntityIdType(entityName, idClassName, stringId), e);
+ }
+ }
+
+ return changes;
+ }
+
+ /** Stores the entity id's as values in the form: entityName:idClass:id */
+ public void postChange(String pageName, String componentId, String fieldName, Object newValue) {
+ if (newValue != null) {
+ try {
+ String entityName = _session.getEntityName(newValue);
+ ClassMetadata metadata = _session.getSessionFactory().getClassMetadata(newValue.getClass());
+ Serializable id = metadata.getIdentifier(newValue, _session.getEntityMode());
+ newValue = entityName + ":" + id.getClass().getCanonicalName() + ":" + _typeCoercer.coerce(id, String.class);
+
+ } catch (HibernateException e) {
+ throw new IllegalArgumentException(HibernateMessages.entityNotAttached(newValue), e);
+ }
+ }
+
+ _strategy.postChange(pageName, componentId, fieldName, newValue);
+ }
+
+ /** We want to store the data in the session normally, we just need to control the values.
+ * We also need a separate instance so that we know it's using the right prefix for the values.
+ */
+ private static final class EntityStrategy extends AbstractSessionPersistentFieldStrategy {
+
+ public EntityStrategy(Request request) {
+ super("entity:", request);
+ }
+
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/HibernateMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/HibernateMessages.java?rev=635133&r1=635132&r2=635133&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/HibernateMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/HibernateMessages.java Sat Mar 8 17:30:20 2008
@@ -37,4 +37,12 @@
static String configurationImmutable() {
return MESSAGES.get("configuration-immutable");
}
+
+ static String badEntityIdType(String entityName, String idClassName, String id) {
+ return MESSAGES.format("bad-entity-id-type", entityName, idClassName, id);
+ }
+
+ static String entityNotAttached(Object entity) {
+ return MESSAGES.format("entity-not-attached", entity);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/main/resources/org/apache/tapestry/internal/hibernate/HibernateStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/resources/org/apache/tapestry/internal/hibernate/HibernateStrings.properties?rev=635133&r1=635132&r2=635133&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/main/resources/org/apache/tapestry/internal/hibernate/HibernateStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/main/resources/org/apache/tapestry/internal/hibernate/HibernateStrings.properties Sat Mar 8 17:30:20 2008
@@ -15,3 +15,5 @@
startup-timing=Hibernate startup: %,d ms to configure, %,d ms overall.
entity-catalog=Configured Hibernate entities: %s
configuration-immutable=The Hibernate configuration is now immutable since the SessionFactory has already been created.
+bad-entity-id-type=Failed to load the entity id class while loading a persisted entity. entity: %s, id class: %s, id: %s
+entity-not-attached=Failed persisting an entity in the session. Only entities attached to a Hibernate Session can be persisted. entity: %s
\ No newline at end of file
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/site/apt/userguide.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/site/apt/userguide.apt?rev=635133&r1=635132&r2=635133&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/site/apt/userguide.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/site/apt/userguide.apt Sat Mar 8 17:30:20 2008
@@ -6,7 +6,7 @@
Value encoders are automatically created for all mapped Hibernate entity types. This is done by encoding the entity as it's
id (coerced to a String) and decoding the entity by looking it up in the Hibernate Session using the encoded id. Consider
- the following:
+ the following example:
+----+
public class ViewPerson {
@@ -17,6 +17,11 @@
{
_person = person;
}
+
+ Person onPassivate()
+ {
+ return _person;
+ }
}
+----+
@@ -29,3 +34,24 @@
+----+
Accessing the page as <</viewperson/152>> would load the Person entity with id 152 and use that as the page context.
+
+Using @Persist with entities
+
+ If you wish to persist an entity in the session, you may use the "entity" persistence strategy:
+
++----+
+public class ViewPerson {
+ @Persist("entity")
+ @Property
+ private Person _person;
+
+ void onActivate(Person person)
+ {
+ _person = person;
+ }
+
+}
++----+
+
+ This persistence strategy works with any Hibernate entity that is associated with a valid Hibernate Session by persisting only the id
+ of the entity. Notice that no onPassivate() method is needed; when the page renders the entity is loaded by the id stored in the session.
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/hibernate/integration/TapestryHibernateIntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/hibernate/integration/TapestryHibernateIntegrationTests.java?rev=635133&r1=635132&r2=635133&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/hibernate/integration/TapestryHibernateIntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/hibernate/integration/TapestryHibernateIntegrationTests.java Sat Mar 8 17:30:20 2008
@@ -17,7 +17,7 @@
import org.apache.tapestry.test.AbstractIntegrationTestSuite;
import org.testng.annotations.Test;
-@Test
+@Test(sequential=true,groups="integration")
public class TapestryHibernateIntegrationTests extends AbstractIntegrationTestSuite
{
public TapestryHibernateIntegrationTests()
@@ -25,7 +25,7 @@
super("src/test/webapp");
}
- public void test_valueencode_all_entity_types() throws Exception {
+ public void valueencode_all_entity_types() throws Exception {
open("/encodeentities");
assertEquals(0, getText("//span[@id='name']").length());
@@ -39,4 +39,29 @@
assertEquals(0, getText("//span[@id='name']").length());
}
+ public void persist_entities() {
+ open("/persistentity");
+ assertEquals(0, getText("//span[@id='name']").length());
+
+ clickAndWait("link=create entity");
+ assertText("//span[@id='name']", "name");
+
+ // shouldn't save the change to the name because it's reloaded every time
+ clickAndWait("link=change the name");
+ assertText("//span[@id='name']", "name");
+
+ // can set back to null
+ clickAndWait("link=set to null");
+ assertEquals(getText("//span[@id='name']").length(), 0);
+
+ // deleting an entity that is still persisted. just remove the entity from the session if it's not found.
+ clickAndWait("link=create entity");
+ assertText("//span[@id='name']", "name");
+ clickAndWait("link=delete");
+ assertEquals(getText("//span[@id='name']").length(), 0);
+
+ // transient objects cannot be persisted
+ clickAndWait("link=set to transient");
+ assertTextPresent("Error persisting");
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategyTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategyTest.java?rev=635133&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategyTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/apache/tapestry/internal/hibernate/EntityPersistentFieldStrategyTest.java Sat Mar 8 17:30:20 2008
@@ -0,0 +1,36 @@
+// Copyright 2006, 2007, 2008 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.tapestry.internal.hibernate;
+
+import org.apache.tapestry.test.TapestryTestCase;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.testng.annotations.Test;
+
+@Test
+public class EntityPersistentFieldStrategyTest extends TapestryTestCase {
+ public void not_an_entity() {
+ Session session = newMock(Session.class);
+ EntityPersistentFieldStrategy strategy = new EntityPersistentFieldStrategy(session, null, null);
+
+ expect(session.getEntityName("foo")).andThrow(new HibernateException("error"));
+ replay();
+ try {
+ strategy.postChange(null, null, null, "foo");
+ fail("did not throw");
+ } catch (IllegalArgumentException e) { }
+ verify();
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/example/app0/pages/PersistEntity.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/example/app0/pages/PersistEntity.java?rev=635133&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/example/app0/pages/PersistEntity.java (added)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/test/java/org/example/app0/pages/PersistEntity.java Sat Mar 8 17:30:20 2008
@@ -0,0 +1,58 @@
+// Copyright 2006, 2007, 2008 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.app0.pages;
+
+import java.util.List;
+
+import org.apache.tapestry.annotations.Persist;
+import org.apache.tapestry.annotations.Property;
+import org.apache.tapestry.ioc.annotations.Inject;
+import org.example.app0.entities.User;
+import org.hibernate.Session;
+
+public class PersistEntity {
+ @Persist("entity")
+ @Property
+ private User _user;
+
+ @Inject
+ private Session _session;
+
+ void onCreateEntity() {
+ User user = new User();
+ user.setFirstName("name");
+ _session.save(user);
+ _user = user;
+ }
+
+ void onChangeName() {
+ _user.setFirstName("name2");
+ // avoid having the changes saved if the request transaction is committed
+ _session.evict(_user);
+ }
+
+ void onSetToTransient() {
+ _user = new User();
+ }
+
+ void onSetToNull() {
+ _user = null;
+ }
+
+ void onDelete() {
+ for(User user : (List<User>)_session.createQuery("from User").list())
+ _session.delete(user);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-hibernate/src/test/webapp/PersistEntity.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/test/webapp/PersistEntity.tml?rev=635133&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/test/webapp/PersistEntity.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/test/webapp/PersistEntity.tml Sat Mar 8 17:30:20 2008
@@ -0,0 +1,10 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+<body>
+ <p>entity name: <span id="name"><t:if test="user">${user.firstName}</t:if></span></p>
+ <p><t:eventlink event="createEntity">create entity</t:eventlink></p>
+ <p><t:eventlink event="changeName">change the name</t:eventlink></p>
+ <p><t:eventlink event="setToNull">set to null</t:eventlink></p>
+ <p><t:eventlink event="delete">delete</t:eventlink></p>
+ <p><t:eventlink event="setToTransient">set to transient</t:eventlink></p>
+</body>
+</html>
\ No newline at end of file