Option to not use typeKey=typeAlias property and predicate. (#1777)

If the configuration getType() or the @TypeAlias is an empty string,
then do not add the typeKey:typeAlias property when storing documents,
and do not add the typeKey=typeAlias property on queries - including
removeByQuery.
This commit is contained in:
Michael Reiche
2023-07-06 16:49:26 -07:00
committed by GitHub
parent 76ef883cca
commit bbcabbc6d4
10 changed files with 287 additions and 29 deletions

View File

@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -38,15 +39,17 @@ import java.util.UUID;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.couchbase.core.ExecutableFindByIdOperation.ExecutableFindById;
import org.springframework.data.couchbase.core.ExecutableRemoveByIdOperation.ExecutableRemoveById;
import org.springframework.data.couchbase.core.ExecutableReplaceByIdOperation.ExecutableReplaceById;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteria;
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
import org.springframework.data.couchbase.core.support.OneAndAllId;
import org.springframework.data.couchbase.core.support.WithDurability;
@@ -66,6 +69,7 @@ import org.springframework.data.couchbase.domain.UserAnnotatedDurabilityExpressi
import org.springframework.data.couchbase.domain.UserAnnotatedPersistTo;
import org.springframework.data.couchbase.domain.UserAnnotatedReplicateTo;
import org.springframework.data.couchbase.domain.UserAnnotatedTouchOnRead;
import org.springframework.data.couchbase.domain.UserNoAlias;
import org.springframework.data.couchbase.domain.UserSubmission;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
@@ -142,6 +146,27 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
}
@Test
void findByIdNoAlias() {
String firstname = UUID.randomUUID().toString();
try {
UserNoAlias user = new UserNoAlias("1", firstname, "user1");
couchbaseTemplate.upsertById(UserNoAlias.class).one(user);
UserNoAlias foundUser = couchbaseTemplate.findById(UserNoAlias.class).one(user.getId());
user.setVersion(foundUser.getVersion());// version will have changed
assertEquals(user, foundUser);
Query query = new Query(QueryCriteria.where(i("firstname")).eq(firstname));
List<UserNoAlias> queriedUsers = couchbaseTemplate.findByQuery(UserNoAlias.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).matching(query).all();
assertEquals(1, queriedUsers.size(), "should have found exactly one");
} finally {
Query query = new Query(QueryCriteria.where(i("firstname")).eq(firstname));
List<RemoveResult> removeResult = couchbaseTemplate.removeByQuery(UserNoAlias.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).matching(query).all();
assertEquals(1, removeResult.size(), "should have removed exactly one");
}
}
@Test
void findByIdWithExpiry() {
try {
@@ -1289,6 +1314,7 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
}
@Test
@Disabled // it's finding _txn documents with source = a single 0 byte which fails to deserialize
void sampleScan() {
String id = "A";
String lower = null;

View File

@@ -63,6 +63,7 @@ import org.springframework.data.couchbase.domain.Address;
import org.springframework.data.couchbase.domain.Config;
import org.springframework.data.couchbase.domain.Person;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserNoAlias;
import org.springframework.data.mapping.MappingException;
/**
@@ -75,10 +76,17 @@ public class MappingCouchbaseConverterTests {
private static MappingCouchbaseConverter converter = new MappingCouchbaseConverter();
private static MappingCouchbaseConverter customConverter = (new Config()).mappingCouchbaseConverter();
private static MappingCouchbaseConverter noTypeKeyConverter = (new Config(){
@Override
public String typeKey() {
return "";
}
}).mappingCouchbaseConverter();
static {
converter.afterPropertiesSet();
customConverter.afterPropertiesSet();
noTypeKeyConverter.afterPropertiesSet();
}
@Test
@@ -152,6 +160,43 @@ public class MappingCouchbaseConverterTests {
assertThat(converted.attr0).isEqualTo(source.get("attr0"));
}
@Test
void writesStringNoTypeKey() {
CouchbaseDocument converted = new CouchbaseDocument();
StringEntity entity = new StringEntity("foobar");
noTypeKeyConverter.write(entity, converted);
Map<String, Object> result = converted.export();
assertThat(result.get("_class")).isEqualTo(null);
assertThat(result.get("attr0")).isEqualTo(entity.attr0);
assertThat(converted.getId()).isEqualTo(BaseEntity.ID);
}
@Test
void readsStringNoTypeKey() {
CouchbaseDocument source = new CouchbaseDocument();
source.put("attr0", "foobar");
StringEntity converted = noTypeKeyConverter.read(StringEntity.class, source);
assertThat(converted.attr0).isEqualTo(source.get("attr0"));
}
@Test
void writesNoTypeAlias() {
CouchbaseDocument converted = new CouchbaseDocument();
UserNoAlias entity = new UserNoAlias(UUID.randomUUID().toString(), "first", "last");
noTypeKeyConverter.write(entity, converted);
Map<String, Object> result = converted.export();
assertThat(result.get("_class")).isEqualTo(null);
assertThat(converted.getId()).isEqualTo(entity.getId());
}
@Test
void readsNoTypeAlias() {
CouchbaseDocument document = new CouchbaseDocument("001");
UserNoAlias user = noTypeKeyConverter.read(UserNoAlias.class, document);
assertThat(user.getId()).isEqualTo("001");
}
@Test
void writesBigInteger() {
CouchbaseDocument converted = new CouchbaseDocument();

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2012-2023 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.domain;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.annotation.Version;
import org.springframework.data.couchbase.core.mapping.Document;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* User entity with an empty TypeAlias for tests
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@Document
@TypeAlias("")
public class UserNoAlias extends AbstractUser implements Serializable {
public JsonNode jsonNode;
public JsonObject jsonObject;
public JsonArray jsonArray;
@PersistenceConstructor
public UserNoAlias(final String id, final String firstname, final String lastname) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.subtype = AbstractingTypeMapper.Type.USER;
this.jsonNode = new ObjectNode(JsonNodeFactory.instance);
try {
jsonNode = (new ObjectNode(JsonNodeFactory.instance)).put("myNumber", uid());
} catch (Exception e) {
e.printStackTrace();
}
Map map = new HashMap();
map.put("myNumber", uid());
this.jsonObject = JsonObject.jo().put("yourNumber",Long.valueOf(uid()));
this.jsonArray = JsonArray.from(Long.valueOf(uid()), Long.valueOf(uid()));
}
@Transient int uid=1000;
long uid(){
return uid++;
}
@Version protected long version;
@Transient protected String transientInfo;
@CreatedBy protected String createdBy;
@CreatedDate protected long createdDate;
@LastModifiedBy protected String lastModifiedBy;
@LastModifiedDate protected long lastModifiedDate;
public String getLastname() {
return lastname;
}
public long getCreatedDate() {
return createdDate;
}
public void setCreatedDate(long createdDate) {
this.createdDate = createdDate;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public long getLastModifiedDate() {
return lastModifiedDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
@Override
public int hashCode() {
return Objects.hash(getId(), firstname, lastname);
}
public String getTransientInfo() {
return transientInfo;
}
public void setTransientInfo(String something) {
transientInfo = something;
}
}