Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
*
* @author Michael Reiche
* @author Emilien Bevierre
* @author Artur Kalimullin
*/
@Stability.Internal
public abstract class AbstractTemplateSupport {
Expand Down Expand Up @@ -191,16 +192,18 @@ CouchbasePersistentEntity couldBePersistentEntity(Class<?> entityClass) {
return null;
}

public <T> T applyResultBase(T entity, CouchbaseDocument converted, Object id, long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(entity);
@SuppressWarnings("unchecked")
public <T> T applyResultBase(CouchbaseDocument converted, long cas, Object txResultHolder,
CouchbaseResourceHolder holder) {
Object entityToWrite = converted.getEntityToWrite();
ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(entityToWrite);

CouchbasePersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(entity.getClass());
.getRequiredPersistentEntity(entityToWrite.getClass());

CouchbasePersistentProperty idProperty = persistentEntity.getIdProperty();
CouchbasePersistentProperty idProperty = persistentEntity.getIdProperty();
if (idProperty != null) {
accessor.setProperty(idProperty, id);
accessor.setProperty(idProperty, converted.getId());
}

CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* @author Jorge Rodriguez Martin
* @author Carlos Espinaco
* @author Emilien Bevierre
* @author Artur Kalimullin
* @since 3.0
*/
class CouchbaseTemplateSupport extends AbstractTemplateSupport implements ApplicationContextAware, TemplateSupport {
Expand All @@ -59,6 +60,7 @@ public CouchbaseDocument encodeEntity(final Object entityToEncode) {
Object maybeNewEntity = maybeCallBeforeConvert(entityToEncode, "");
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(maybeNewEntity, converted);
converted.setEntityToWrite(maybeNewEntity);
maybeCallAfterConvert(entityToEncode, converted, "");
maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted));
return converted;
Expand All @@ -77,9 +79,9 @@ public <T> T decodeEntity(Object id, byte[] source, Long cas, Instant expiryTime
}

@Override
public <T> T applyResult(T entity, CouchbaseDocument converted, Object id, long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
return applyResultBase(entity, converted, id, cas, txResultHolder, holder);
public <T> T applyResult(CouchbaseDocument converted, long cas, Object txResultHolder,
CouchbaseResourceHolder holder) {
return applyResultBase(converted, cas, txResultHolder, holder);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* @author Carlos Espinaco
* @author Michael Reiche
* @author Emilien Bevierre
* @author Artur Kalimullin
* @since 4.2
*/
public class NonReactiveSupportWrapper implements ReactiveTemplateSupport {
Expand Down Expand Up @@ -59,9 +60,9 @@ public <T> Mono<T> decodeEntity(Object id, byte[] source, Long cas, Instant expi
}

@Override
public <T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> support.applyResult(entity, converted, id, cas, txResultHolder, holder));
public <T> Mono<T> applyResult(CouchbaseDocument converted, Long cas, Object txResultHolder,
CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> support.applyResult(converted, cas, txResultHolder, holder));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
* @author Carlos Espinaco
* @author Michael Reiche
* @author Emilien Bevierre
* @author Artur Kalimullin
* @since 4.2
*/
class ReactiveCouchbaseTemplateSupport extends AbstractTemplateSupport
Expand All @@ -61,6 +62,7 @@ public Mono<CouchbaseDocument> encodeEntity(final Object entityToEncode) {
.flatMap(entity -> maybeCallBeforeConvert(entity, "")).map(maybeNewEntity -> {
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(maybeNewEntity, converted);
converted.setEntityToWrite(maybeNewEntity);
return converted;
}).flatMap(converted -> maybeCallAfterConvert(entityToEncode, converted, "").thenReturn(converted))
.doOnNext(converted -> maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted)));
Expand Down Expand Up @@ -88,9 +90,9 @@ public <T> Mono<T> decodeEntity(Object id, byte[] source, Long cas, Instant expi
}

@Override
public <T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
Object txResultHolder, CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> applyResultBase(entity, converted, id, cas, txResultHolder, holder));
public <T> Mono<T> applyResult(CouchbaseDocument converted, Long cas, Object txResultHolder,
CouchbaseResourceHolder holder) {
return Mono.fromSupplier(() -> this.applyResultBase(converted, cas, txResultHolder, holder));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
* @author Michael Reiche
* @author Tigran Babloyan
* @author Emilien Bevierre
* @author Artur Kalimullin
*/
public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOperation {

Expand Down Expand Up @@ -107,7 +108,7 @@ public Mono<T> one(T object) {
return collection.reactive()
.insert(converted.getId().toString(), converted.export(),
buildOptions(pArgs.getOptions(), converted))
.flatMap(result -> this.support.applyResult(object, converted, converted.getId(), result.cas(),
.flatMap(result -> this.support.<T>applyResult(converted, result.cas(),
null, null));
} else {
rejectInvalidTransactionalOptions();
Expand All @@ -120,7 +121,7 @@ public Mono<T> one(T object) {
template.getCouchbaseClientFactory().getCluster().environment().transcoder()
.encode(converted.export()).encoded(),
new SpanWrapper(span))
.flatMap(result -> this.support.applyResult(object, converted, converted.getId(), result.cas(),
.flatMap(result -> this.support.applyResult(converted, result.cas(),
null, null));
}
})).onErrorMap(throwable -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* {@link ReactiveMutateInByIdOperation} implementations for Couchbase.
*
* @author Tigran Babloyan
* @author Artur Kalimullin
*/
public class ReactiveMutateInByIdOperationSupport implements ReactiveMutateInByIdOperation {

Expand Down Expand Up @@ -119,7 +120,7 @@ public Mono<T> one(T object) {
.flatMap(collection -> collection.reactive()
.mutateIn(converted.getId().toString(), getMutations(converted), buildMutateInOptions(pArgs.getOptions(), object, converted))
.flatMap(
result -> support.applyResult(object, converted, converted.getId(), result.cas(), null, null)));
result -> support.applyResult(converted, result.cas(), null, null)));
});

return reactiveEntity.onErrorMap(throwable -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
* @author Michael Reiche
* @author Tigran Babloyan
* @author Emilien Bevierre
* @author Artur Kalimullin
*/
public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdOperation {

Expand Down Expand Up @@ -110,7 +111,7 @@ public Mono<T> one(T object) {
return collection.reactive()
.replace(converted.getId().toString(), converted.export(),
buildReplaceOptions(pArgs.getOptions(), object, converted))
.flatMap(result -> support.applyResult(object, converted, converted.getId(), result.cas(), null,
.flatMap(result -> support.applyResult(converted, result.cas(), null,
null));
} else {
rejectInvalidTransactionalOptions();
Expand All @@ -137,7 +138,7 @@ public Mono<T> one(T object) {
return ctx.replace(getResult, template.getCouchbaseClientFactory().getCluster().environment()
.transcoder().encode(converted.export()).encoded(), new SpanWrapper(span));
}).flatMap(
result -> support.applyResult(object, converted, converted.getId(), result.cas(), null, null));
result -> support.<T>applyResult(converted, result.cas(), null, null));
}
})).onErrorMap(throwable -> {
if (throwable instanceof RuntimeException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
*
* @author Michael Reiche
* @author Emilien Bevierre
* @author Artur Kalimullin
*/
public interface ReactiveTemplateSupport {

Expand All @@ -43,8 +44,8 @@ default <T> Mono<T> decodeEntity(Object id, byte[] source, Long cas, Instant exp
collection, txResultHolder, holder);
}

<T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
Object txResultHolder, CouchbaseResourceHolder holder);
<T> Mono<T> applyResult(CouchbaseDocument converted, Long cas, Object txResultHolder,
CouchbaseResourceHolder holder);

Long getCas(Object entity);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
*
* @author Michael Reiche
* @author Tigran Babloyan
* @author Artur Kalimullin
*/
public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOperation {

Expand Down Expand Up @@ -99,7 +100,7 @@ public Mono<T> one(T object) {
.flatMap(collection -> collection.reactive()
.upsert(converted.getId().toString(), converted.export(), buildUpsertOptions(pArgs.getOptions(), converted))
.flatMap(
result -> support.applyResult(object, converted, converted.getId(), result.cas(), null, null)));
result -> support.applyResult(converted, result.cas(), null, null)));
});

return reactiveEntity.onErrorMap(throwable -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
/**
* @author Michael Reiche
* @author Emilien Bevierre
* @author Artur Kalimullin
*/
public interface TemplateSupport {

Expand All @@ -40,8 +41,7 @@ default <T> T decodeEntity(Object id, byte[] source, Long cas, Instant expiryTim
collection, txResultHolder, holder);
}

<T> T applyResult(T entity, CouchbaseDocument converted, Object id, long cas, Object txResultHolder,
CouchbaseResourceHolder holder);
<T> T applyResult(CouchbaseDocument converted, long cas, Object txResultHolder, CouchbaseResourceHolder holder);

Long getCas(Object entity);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* topmost document most likely has an ID.
*
* @author Michael Nitschinger
* @author Artur Kalimullin
*/
public class CouchbaseDocument implements CouchbaseStorable {

Expand All @@ -56,6 +57,11 @@ public class CouchbaseDocument implements CouchbaseStorable {
*/
private int expiration;

/**
* Contains the entity encoded into this document.
*/
private Object entityToWrite;

/**
* Creates a completely empty {@link CouchbaseDocument}.
*/
Expand Down Expand Up @@ -260,6 +266,26 @@ public CouchbaseDocument setId(Object id) {
return this;
}

/**
* Returns the entity encoded into this document.
*
* @return the entity encoded into this document.
*/
public Object getEntityToWrite() {
return entityToWrite;
}

/**
* Sets the entity encoded into this document.
*
* @param entityToWrite the entity encoded into this document.
* @return this document.
*/
public CouchbaseDocument setEntityToWrite(Object entityToWrite) {
this.entityToWrite = entityToWrite;
return this;
}

/**
* Verifies that only values of a certain and supported type can be stored.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* {@link EntityCallback} to populate auditing related fields on an entity about to be saved.
*
* @author Jorge Rodríguez Martín
* @author Artur Kalimullin
* @since 4.2
*/
public class AuditingEntityCallback implements BeforeConvertCallback<Object>, AfterConvertCallback<Object>, Ordered {
Expand All @@ -54,9 +55,7 @@ public AuditingEntityCallback(ObjectFactory<IsNewAwareAuditingHandler> auditingH
*/
@Override
public Object onBeforeConvert(Object entity, String collection) {
// LOG.trace("onBeforeConvert " + entity);
return entity; // markAudited called in AuditingEventListener.onApplicationEvent()
// auditingHandlerFactory.getObject().markAudited(entity);
return auditingHandlerFactory.getObject().markAudited(entity);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.event.AuditingEntityCallback;
import org.springframework.data.couchbase.core.mapping.event.AuditingEventListener;
import org.springframework.util.Assert;

/**
Expand All @@ -44,6 +43,7 @@
* @author Simon Baslé
* @author Michael Reiche
* @author Jorge Rodríguez Martín
* @author Artur Kalimullin
*/
public class CouchbaseAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {

Expand Down Expand Up @@ -85,8 +85,6 @@ protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandle
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");

// Register the AuditEntityCallback

BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AuditingEntityCallback.class);
listenerBeanDefinitionBuilder
Expand All @@ -95,16 +93,6 @@ protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandle
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEntityCallback.class.getName(), registry);

// Register the AuditingEventListener

BeanDefinitionBuilder listenerBeanDefinitionBuilder2 = BeanDefinitionBuilder
.rootBeanDefinition(AuditingEventListener.class);
listenerBeanDefinitionBuilder2
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));

registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder2.getBeanDefinition(),
AuditingEventListener.class.getName(), registry);

}

private void ensureMappingContext(BeanDefinitionRegistry registry, Object source) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2026 the original author or authors.
*
* 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
*
* https://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.springframework.data.couchbase.core.mapping.event;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;

import java.time.Instant;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.domain.AuditedRecord;

/**
* @author Artur Kalimullin
*/
class AuditingEntityCallbackTests {

@Test
void returnsAuditedImmutableEntity() {
Instant now = Instant.parse("2026-08-12T09:00:00Z");
IsNewAwareAuditingHandler auditingHandler = IsNewAwareAuditingHandler.from(new CouchbaseMappingContext());
auditingHandler.setDateTimeProvider(() -> Optional.of(now));

AuditedRecord original = new AuditedRecord("id", 0, null, null, "value");
AuditingEntityCallback callback = new AuditingEntityCallback(() -> auditingHandler);
AuditedRecord audited = (AuditedRecord) callback.onBeforeConvert(original, "collection");

assertNotSame(original, audited);
assertEquals(now, audited.createdDate());
assertEquals(now, audited.lastModifiedDate());
}
}
Loading