Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge changes from 4.9.x #3146

Merged
merged 8 commits into from
Sep 26, 2024
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.micronaut.data.jdbc.h2.remap;

import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import io.micronaut.data.annotation.Relation;

import java.util.List;
import java.util.UUID;

import static io.micronaut.data.annotation.Relation.Cascade.ALL;
import static io.micronaut.data.annotation.Relation.Kind.MANY_TO_MANY;

@MappedEntity("att_course")
record Course(

@Id
UUID id,

String name,

@Relation(value = MANY_TO_MANY, mappedBy = "courses", cascade = ALL)
List<Student> students
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.micronaut.data.jdbc.h2.remap;

import io.micronaut.data.annotation.Join;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

import java.util.List;
import java.util.UUID;

@JdbcRepository(dialect = Dialect.H2)
interface CourseRepository extends CrudRepository<Course, UUID> {

@Join("students")
List<Student> findStudentsById(UUID id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.micronaut.data.jdbc.h2.remap

import io.micronaut.data.jdbc.h2.H2DBProperties
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Specification

@H2DBProperties
@MicronautTest
class ManyToManyAttributeSpec extends Specification {

@Inject
CourseRepository courseRepository
@Inject
StudentRepository studentRepository

def "works - should create a student"() {
when:
Student student = new Student(
new StudentId(UUID.randomUUID()),
"test",
List.of()
)
studentRepository.save(student)
then:
studentRepository.findById(student.id()).get() == student
}

def "should find students attending a course"() {
when:
Course course = new Course(
UUID.randomUUID(),
"computer science",
List.of()
)
courseRepository.save(course)
// create a new student and join the existing course
Student student = new Student(
new StudentId(UUID.randomUUID()),
"test",
List.of(course)
)
studentRepository.save(student) == student

then:
// we should now be able to find the student that attends the course
courseRepository.findStudentsById(course.id())[0].name() == student.name()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.micronaut.data.jdbc.h2.remap;

import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import io.micronaut.data.annotation.MappedProperty;
import io.micronaut.data.annotation.Relation;
import io.micronaut.data.model.DataType;

import java.util.List;

import static io.micronaut.data.annotation.Relation.Cascade.ALL;
import static io.micronaut.data.annotation.Relation.Kind.MANY_TO_MANY;

@MappedEntity("att_student")
record Student(

@Id
@MappedProperty(converter = StudentIdAttributeConverter.class, type = DataType.UUID)
StudentId id,

String name,

@Relation(value = MANY_TO_MANY, cascade = ALL)
List<Course> courses
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.micronaut.data.jdbc.h2.remap;

import io.micronaut.core.convert.ConversionContext;
import io.micronaut.data.model.runtime.convert.AttributeConverter;
import jakarta.inject.Singleton;

import java.util.UUID;

record StudentId(UUID id) {
}

@Singleton
class StudentIdAttributeConverter implements AttributeConverter<StudentId, UUID> {
public UUID convertToPersistedValue(StudentId entityValue, ConversionContext context) {
return entityValue.id();
}

public StudentId convertToEntityValue(UUID persistedValue, ConversionContext context) {
return new StudentId(persistedValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.micronaut.data.jdbc.h2.remap;

import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

@JdbcRepository(dialect = Dialect.H2)
interface StudentRepository extends CrudRepository<Student, StudentId> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class DefaultPage<T> extends DefaultSlice<T> implements Page<T> {

@Override
public boolean hasTotalSize() {
return totalSize != null;
return totalSize != null && totalSize != -1L;
}

@Override
Expand Down
3 changes: 2 additions & 1 deletion data-model/src/main/java/io/micronaut/data/model/Page.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
@DefaultImplementation(DefaultPage.class)
public interface Page<T> extends Slice<T> {

Page<?> EMPTY = new DefaultPage<>(Collections.emptyList(), Pageable.unpaged(), null);
Page<?> EMPTY = new DefaultPage<>(Collections.emptyList(), Pageable.unpaged(), -1L);

/**
* @return Whether this {@link Page} contains the total count of the records
Expand All @@ -62,6 +62,7 @@ public interface Page<T> extends Slice<T> {
* Get the total count of all the records that can be given by this query.
* The method may produce a {@link IllegalStateException} if the {@link Pageable} request
* did not ask for total size.
* For {@link #EMPTY} page the value is -1.
*
* @return The total size of the all records.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,16 @@ class PageSpec extends Specification {
def json = mapper.writeValueAsString(page)

then:
def deserializedPage = mapper.readValue(json, mapper.typeFactory.constructParametricType(Page, Dummy))
def deserializedPage = (Page<Dummy>) mapper.readValue(json, mapper.typeFactory.constructParametricType(Page, Dummy))
deserializedPage.content.every { it instanceof Dummy }
deserializedPage == page

when:"Serialize and deserialize empty page"
json = mapper.writeValueAsString(Page.empty())

then:"It is done without errors"
def deserializedEmptyPage = (Page<Dummy>) mapper.readValue(json, mapper.typeFactory.constructParametricType(Page, Dummy))
!deserializedEmptyPage.hasTotalSize()
}

void "test serialization and deserialization of a page - serde"() {
Expand All @@ -113,6 +120,13 @@ class PageSpec extends Specification {
def deserializedPage = serdeMapper.readValue(json, Argument.of(Page, Dummy))
deserializedPage.content.every { it instanceof Dummy }
deserializedPage == page

when:"Serialize and deserialize empty page"
json = serdeMapper.writeValueAsString(Page.empty())

then:"It is done without errors"
def deserializedEmptyPage = serdeMapper.readValue(json, Argument.of(Page, Dummy))
!deserializedEmptyPage.hasTotalSize()
}

void "test serialization and deserialization of a pageable - serde"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.beans.BeanIntrospection;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.MappedProperty;
Expand All @@ -39,7 +40,6 @@
import org.bson.BsonDocument;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.Codec;
import org.bson.codecs.IterableCodec;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

Expand Down Expand Up @@ -180,11 +180,14 @@ public <T> Deserializer<? extends T> findDeserializer(Argument<? extends T> type
if (codec instanceof MappedCodec<? extends T> mappedCodec) {
return mappedCodec.deserializer;
}
if (codec != null
&& !(codec instanceof IterableCodec)
&& !(Map.class.isAssignableFrom(codec.getEncoderClass()))
&& !(Collection.class.isAssignableFrom(codec.getEncoderClass()))) {
return new CodecBsonDecoder<>((Codec<T>) codec);
if (codec != null) {
// Eliminate codecs for basic types and collections
Class<? extends T> encoderClass = codec.getEncoderClass();
if (!ClassUtils.isJavaLangType(encoderClass)
&& !Map.class.isAssignableFrom(encoderClass)
&& !Iterable.class.isAssignableFrom(encoderClass)) {
return new CodecBsonDecoder<>((Codec<T>) codec);
}
}
return parent.findDeserializer(type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.MappedProperty;
Expand All @@ -35,7 +36,6 @@
import io.micronaut.serde.reference.PropertyReference;
import io.micronaut.serde.reference.SerializationReference;
import org.bson.codecs.Codec;
import org.bson.codecs.IterableCodec;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

Expand Down Expand Up @@ -164,8 +164,14 @@ public <T> Serializer<? super T> findSerializer(Argument<? extends T> type) thro
if (codec instanceof MappedCodec mappedCodec) {
return mappedCodec.serializer;
}
if (codec != null && !(codec instanceof IterableCodec) && !(Map.class.isAssignableFrom(codec.getEncoderClass()))) {
return new CodecBsonDecoder<>((Codec<T>) codec);
if (codec != null) {
// Eliminate codecs for basic types and collections
Class<? extends T> encoderClass = codec.getEncoderClass();
if (!ClassUtils.isJavaLangType(encoderClass)
&& !Map.class.isAssignableFrom(encoderClass)
&& !Iterable.class.isAssignableFrom(encoderClass)) {
return new CodecBsonDecoder<>((Codec<T>) codec);
}
}
return parent.findSerializer(type);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.micronaut.data.document.mongodb

import groovy.transform.CompileStatic
import io.micronaut.context.ApplicationContext
import io.micronaut.core.annotation.Introspected
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.data.mongodb.annotation.MongoRepository
import io.micronaut.data.repository.CrudRepository
import io.micronaut.serde.annotation.Serdeable
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import jakarta.persistence.Id
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

@MicronautTest
class MongoRuntimeSpec extends Specification implements MongoTestPropertyProvider {
@AutoCleanup
@Shared
ApplicationContext applicationContext = ApplicationContext.run(getProperties())

@Shared
@Inject
TestContentRepository contentRepository = applicationContext.getBean(TestContentRepository)

def cleanup() {
contentRepository.deleteAll()
}

void 'test runtime serialization'() {
given:
TestContent testContent = new TestContent(
id: 'test',
category: [
one : 'one',
two : 2,
three: false
]
)

when:
contentRepository.save(testContent)
then:
contentRepository.findById('test').get().category == testContent.category
}

}

@CompileStatic
@MongoRepository
interface TestContentRepository extends CrudRepository<TestContent, String> {
}

@CompileStatic
@Serdeable
@MappedEntity
@Introspected
class TestContent {

@Id
String id

BigDecimal number


String propertyId = null // Property name.


Map category = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ protected <T> Predicate interceptPredicate(MethodMatchContext matchContext,
final SourcePersistentEntity rootEntity = (SourcePersistentEntity) root.getPersistentEntity();
Predicate predicate = null;
if (entityParameter != null) {
if (rootEntity.getVersion() != null) {
if (rootEntity.getVersion() != null && existingPredicate == null) {
predicate = cb.and(
cb.equal(root.id(), cb.entityPropertyParameter(entityParameter, null)),
cb.equal(root.version(), cb.entityPropertyParameter(entityParameter, null))
);
} else {
} else if (existingPredicate == null) {
predicate = cb.equal(root.id(), cb.entityPropertyParameter(entityParameter, null));
}
} else {
Expand Down
Loading
Loading