You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We often want to do stuff before an entity is stored in the database. A common case is that we want to keep track of the timestamp when an entity was last updated. For this, we have previously been overriding the insert() method:
This is usually enough for our purposes, even though it's not pretty. One thing though that bothers us though is that we're never 100% sure that the method will actually be called on insert. If we call save() instead of insert(), our code is never executed. Also, if we make a batch insert through Model.insert(), our code is never executed.
We'd been thinking about adding annotations as a way of doing this instead of overriding. Then we came across the Lifecycle support which looked exactly like what we wanted. But it doesn't behave the way we though it would. Most importantly: why is there a need for a @presave annotation? save() is a handy way of inserting or updating an entity without caring whether it has been inserted before. But it should behave as if you would either call insert() or update(). With the current implementation, we have to tell developers not to use save() for the side effects they may cause.
Below is a test case demonstrating what's wrong (same test case as for the other lifecycle bug I added). For this issue, look at the preUpdateAndPreInsertShouldBeCalledOnSave() method.
packagesiena.lifecycle.test;
importjava.util.Date;
importjunit.framework.Assert;
importorg.junit.After;
importorg.junit.Before;
importorg.junit.Test;
importsiena.Generator;
importsiena.Id;
importsiena.Model;
importsiena.PersistenceManager;
importsiena.PersistenceManagerFactory;
importsiena.core.PersistenceManagerLifeCycleWrapper;
importsiena.core.lifecycle.PreInsert;
importsiena.core.lifecycle.PreUpdate;
importsiena.gae.GaePersistenceManager;
importcom.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
importcom.google.appengine.tools.development.testing.LocalServiceTestHelper;
importcom.google.appengine.tools.development.testing.LocalTaskQueueTestConfig;
publicclassLifeCycleTest {
/** * A simple model class with lifecycle annotations * * @author henper * */publicstaticclassLifeCycleEntityextendsModel {
@Id(Generator.AUTO_INCREMENT)
publicLongid;
publicDatecreated;
publicDateupdated;
public@PreInsertvoidonInsert() {
created = newDate();
}
public@PreUpdate@PreInsertvoidonUpdate() {
updated = newDate();
}
}
@TestpublicvoidpreInsertShouldWorkOnBatchInsert() {
// If you are storing multiple entities at once, a batch insert is handy:LifeCycleEntityone = newLifeCycleEntity();
LifeCycleEntitytwo = newLifeCycleEntity();
Model.batch(LifeCycleEntity.class).insert(one, two);
// It is expected that the @PreInsert is called once for each entity, before inserting them:Assert.assertNotNull("The \"created\" date was not updated for entity \"one\"", one.created);
Assert.assertNotNull("The \"created\" date was not updated for entity \"two\"", two.created);
}
@TestpublicvoidpreUpdateAndPreInsertShouldBeCalledOnSave() {
LifeCycleEntityone = newLifeCycleEntity();
one.save();
Assert.assertNotNull("The \"created\" date was not updated on save()", one.created);
Assert.assertNotNull("The \"updated\" date was not updated on save()", one.updated);
}
@TestpublicvoidpreInsertShouldBeCalledOnInsert() {
LifeCycleEntityone = newLifeCycleEntity();
one.insert();
Assert.assertNotNull("The \"created\" date was not updated on insert()", one.created);
Assert.assertNotNull("The \"updated\" date was not updated on insert()", one.updated);
}
@TestpublicvoidpreUpdateShouldBeCalledOnUpdate() {
LifeCycleEntityone = newLifeCycleEntity();
one.insert();
one.update();
Assert.assertNotNull("The \"created\" date was not updated on update()", one.created);
Assert.assertNotNull("The \"updated\" date was not updated on update()", one.updated);
}
@BeforepublicvoidsetUp() throwsException {
helper.setUp();
}
@AfterpublicvoidtearDown() throwsException {
helper.tearDown();
}
@BeforepublicvoidenableLifeCycleSupport() {
PersistenceManagerpm = newGaePersistenceManager();
pm = newPersistenceManagerLifeCycleWrapper(pm);
pm.init(null);
PersistenceManagerFactory.install(pm, LifeCycleEntity.class);
}
privatefinalLocalServiceTestHelperhelper = newLocalServiceTestHelper(newLocalDatastoreServiceTestConfig(), newLocalTaskQueueTestConfig());
}
The text was updated successfully, but these errors were encountered:
We often want to do stuff before an entity is stored in the database. A common case is that we want to keep track of the timestamp when an entity was last updated. For this, we have previously been overriding the insert() method:
This is usually enough for our purposes, even though it's not pretty. One thing though that bothers us though is that we're never 100% sure that the method will actually be called on insert. If we call save() instead of insert(), our code is never executed. Also, if we make a batch insert through Model.insert(), our code is never executed.
We'd been thinking about adding annotations as a way of doing this instead of overriding. Then we came across the Lifecycle support which looked exactly like what we wanted. But it doesn't behave the way we though it would. Most importantly: why is there a need for a @presave annotation? save() is a handy way of inserting or updating an entity without caring whether it has been inserted before. But it should behave as if you would either call insert() or update(). With the current implementation, we have to tell developers not to use save() for the side effects they may cause.
Below is a test case demonstrating what's wrong (same test case as for the other lifecycle bug I added). For this issue, look at the preUpdateAndPreInsertShouldBeCalledOnSave() method.
The text was updated successfully, but these errors were encountered: