diff --git a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java index e7348c37..7a0222c5 100644 --- a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java +++ b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java @@ -402,6 +402,22 @@ public class CouchbaseTemplateTests { assertEquals(versionedClass.getVersion(), foundClass.getVersion()); } + /** + * @see DATACOUCH-59 + */ + @Test + public void expiryWhenTouchOnReadDocument() throws InterruptedException { + String id = "simple-doc-with-update-expiry-for-read"; + DocumentWithTouchOnRead doc = new DocumentWithTouchOnRead(id); + template.save(doc); + Thread.sleep(1500); + assertNotNull(template.findById(id, DocumentWithTouchOnRead.class)); + Thread.sleep(1500); + assertNotNull(template.findById(id, DocumentWithTouchOnRead.class)); + Thread.sleep(3000); + assertNull(template.findById(id, DocumentWithTouchOnRead.class)); + } + /** * A sample document with just an id and property. */ @@ -433,6 +449,20 @@ public class CouchbaseTemplateTests { } } + /** + * A sample document that expires in 2 seconds and touchOnRead set. + */ + @Document(expiry = 2, touchOnRead = true) + static class DocumentWithTouchOnRead { + + @Id + private final String id; + + public DocumentWithTouchOnRead(String id) { + this.id = id; + } + } + @Document static class ComplexPerson { diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 4384fc22..9c8885d4 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -281,10 +281,15 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP @Override public T findById(final String id, Class entityClass) { + final CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(entityClass); RawJsonDocument result = execute(new BucketCallback() { @Override public RawJsonDocument doInBucket() { - return client.get(id, RawJsonDocument.class); + if (entity.isTouchOnRead()) { + return client.getAndTouch(id, entity.getExpiry(), RawJsonDocument.class); + } else { + return client.get(id, RawJsonDocument.class); + } } }); diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java index 10c8cb46..a70e07be 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java @@ -16,12 +16,7 @@ package org.springframework.data.couchbase.core.mapping; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - import com.couchbase.client.java.repository.annotation.Id; - import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -31,6 +26,10 @@ import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; import org.springframework.expression.spel.support.StandardEvaluationContext; +import java.util.Calendar; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + /** * The representation of a persistent entity. * @@ -123,4 +122,11 @@ public class BasicCouchbasePersistentEntity extends BasicPersistentEntity 0 && annotation.touchOnRead(); + } + } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java index d5f09020..e7712bd4 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java @@ -42,4 +42,12 @@ public interface CouchbasePersistentEntity extends */ int getExpiry(); + /** + * Flag for using getAndTouch operations for reads, resetting the expiration (if one was set) when the + * entity is directly read (eg. findOne, findById). + * + * @return true if a direct read of the document should trigger a touch, resetting its expiration timer. + */ + boolean isTouchOnRead(); + } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java b/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java index a1b6856f..a3f07ee3 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java @@ -46,4 +46,10 @@ public @interface Document { */ TimeUnit expiryUnit() default TimeUnit.SECONDS; + /** + * An optional flag associated with {@link #expiry()} indicating whether the expiry timer should + * be reset whenever the document is directly read (eg. findByOne, findById). + */ + boolean touchOnRead() default false; + } diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java b/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java index 38ce03c6..2ee24941 100644 --- a/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java @@ -17,6 +17,8 @@ package org.springframework.data.couchbase.core.mapping; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.Calendar; import java.util.Date; @@ -103,6 +105,42 @@ public class BasicCouchbasePersistentEntityTests { assertEquals(expected.get(Calendar.SECOND), calendar.get(Calendar.SECOND)); } + @Test + public void doesNotUseGetExpiry() throws Exception { + assertEquals(0, getBasicCouchbasePersistentEntity(SimpleDocument.class).getExpiry()); + } + + @Test + public void usesGetExpiry() throws Exception { + assertEquals(10, getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class).getExpiry()); + } + + @Test + public void doesNotUseIsUpdateExpiryForRead() throws Exception { + assertFalse(getBasicCouchbasePersistentEntity(SimpleDocument.class).isTouchOnRead()); + assertFalse(getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class).isTouchOnRead()); + } + + @Test + public void usesTouchOnRead() throws Exception { + assertTrue(getBasicCouchbasePersistentEntity(SimpleDocumentWithTouchOnRead.class).isTouchOnRead()); + } + + private BasicCouchbasePersistentEntity getBasicCouchbasePersistentEntity(Class clazz) { + return new BasicCouchbasePersistentEntity(ClassTypeInformation.from(clazz)); + } + + public static class SimpleDocument { + } + + @Document(expiry = 10) + public static class SimpleDocumentWithExpiry { + } + + @Document(expiry = 10, touchOnRead = true) + public static class SimpleDocumentWithTouchOnRead { + } + /** * Simple POJO to test default expiry. */