Skip to content

Commit

Permalink
Allow limited no-id entity operations
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov committed May 3, 2021
1 parent 90237aa commit 3f4004f
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,7 @@ private <T, R> Stream<R> findStream(@NonNull PreparedQuery<T, R> preparedQuery,
}
}
);
boolean onlySingleEndedJoins = joinFetchPaths.stream()
.flatMap(jp -> Arrays.stream(jp.getAssociationPath()))
.anyMatch(association -> association.getKind().isSingleEnded());
boolean onlySingleEndedJoins = isOnlySingleEndedJoins(getEntity(preparedQuery.getRootEntity()), joinFetchPaths);
// Cannot stream ResultSet for "many" joined query
if (!onlySingleEndedJoins) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package io.micronaut.data.jdbc.postgres

import io.micronaut.context.ApplicationContext
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.data.jdbc.annotation.JdbcRepository
import io.micronaut.data.model.Pageable
import io.micronaut.data.model.Sort
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.runtime.config.SchemaGenerate
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import javax.inject.Inject
import java.sql.Connection

class PostgresNoIdEntitySpec extends Specification implements PostgresTestPropertyProvider {
@AutoCleanup
@Shared
ApplicationContext applicationContext = ApplicationContext.run(getProperties())

@Shared
@Inject
NoIdEntityRepository noIdEntityRepository = applicationContext.getBean(NoIdEntityRepository)

@Shared
@Inject
Connection connection = applicationContext.getBean(Connection)

void setup() {
connection.prepareStatement('''
drop sequence if exists "no_id_names_seq";
drop table if exists "no_id_names";
create sequence "no_id_names_seq" increment by 1;
create table "no_id_names"
(
"id" bigint default nextval('no_id_names_seq'::regclass) not null,
"first_name" varchar(255) ,
"last_name" varchar(255)
);
''').withCloseable { it.executeUpdate() }
}

void 'test no-id entity operations'() {
given:
when:
noIdEntityRepository.save(new NoIdEntity(firstName: "Xyz", lastName: "Abc"))
noIdEntityRepository.save(new NoIdEntity(firstName: "Qwe", lastName: "Jkl"))
def all = noIdEntityRepository.listAll()
def allPageable = noIdEntityRepository.listAll(Pageable.from(1, 1).order("firstName", Sort.Order.Direction.DESC))
then:
all.size() == 2
all.find { it.firstName == "Xyz" }
all.find { it.firstName == "Qwe" }
allPageable.size() == 1
allPageable.find { it.firstName == "Qwe" }
}

@Override
SchemaGenerate schemaGenerate() {
return SchemaGenerate.NONE
}
}

@JdbcRepository(dialect = Dialect.POSTGRES)
interface NoIdEntityRepository {

NoIdEntity save(NoIdEntity entity)

List<NoIdEntity> listAll()

List<NoIdEntity> listAll(Pageable pageable)

}

@MappedEntity("no_id_names")
class NoIdEntity {

String firstName
String lastName
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,15 @@
*/
package io.micronaut.data.jdbc.postgres

import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.runtime.config.SchemaGenerate
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import io.micronaut.test.support.TestPropertyProvider
import org.testcontainers.containers.PostgreSQLContainer
import spock.lang.Shared
import spock.lang.Specification

import javax.inject.Inject
import java.sql.Connection

@MicronautTest
class PostgresSequenceDefaultSpec extends Specification implements TestPropertyProvider {

@Shared
PostgreSQLContainer postgres

@Override
Map<String, String> getProperties() {
postgres = new PostgreSQLContainer<>("postgres:10")
.withDatabaseName("test-database")
.withUsername("test")
.withPassword("test")
postgres.start()
[
"datasources.default.url":postgres.getJdbcUrl(),
"datasources.default.username":postgres.getUsername(),
"datasources.default.password":postgres.getPassword(),
"datasources.default.dialect": Dialect.POSTGRES
] as Map<String, String>
}
class PostgresSequenceDefaultSpec extends Specification implements PostgresTestPropertyProvider {

@Inject
Connection connection
Expand All @@ -54,6 +33,8 @@ class PostgresSequenceDefaultSpec extends Specification implements TestPropertyP

void setup() {
connection.prepareStatement('''
drop sequence if exists "user_seq";
drop table if exists "user_";
create sequence "user_seq" increment by 1;
create table "user_"
(
Expand All @@ -66,11 +47,6 @@ create table "user_"
''').withCloseable { it.executeUpdate( )}
}

def cleanup() {
postgres.close()
}


void "test sequence generation with default nextval"() {
when:
User user = userRepository.save("Fred")
Expand All @@ -82,4 +58,9 @@ create table "user_"
user.id != user2.id
user.id == userRepository.findById(user.id).id
}

@Override
SchemaGenerate schemaGenerate() {
return SchemaGenerate.CREATE_DROP
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ trait PostgresTestPropertyProvider implements SharedDatabaseContainerTestPropert

@Override
int sharedSpecsCount() {
return 9
return 11
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -662,9 +662,7 @@ public <T, R> Flux<R> findAll(@NonNull PreparedQuery<T, R> preparedQuery) {
}
}
);
boolean onlySingleEndedJoins = joinFetchPaths.stream()
.flatMap(jp -> Arrays.stream(jp.getAssociationPath()))
.anyMatch(association -> association.getKind().isSingleEnded());
boolean onlySingleEndedJoins = isOnlySingleEndedJoins(getEntity(preparedQuery.getRootEntity()), joinFetchPaths);
// Cannot stream ResultSet for "many" joined query
if (!onlySingleEndedJoins) {
SqlResultEntityTypeMapper.PushingMapper<Row, List<R>> manyReader = entityTypeMapper.readAllWithJoins();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import io.micronaut.data.model.PersistentProperty;
import io.micronaut.data.model.PersistentPropertyPath;
import io.micronaut.data.model.Sort;
import io.micronaut.data.model.query.JoinPath;
import io.micronaut.data.model.query.QueryModel;
import io.micronaut.data.model.query.QueryParameter;
import io.micronaut.data.model.query.builder.AbstractSqlLikeQueryBuilder;
Expand Down Expand Up @@ -1261,6 +1262,29 @@ protected void checkOptimisticLocking(int expected, int received) {
}
}

/**
* Check if joined associated are all single ended (Can produce only one result).
*
* @param rootPersistentEntity The root entity
* @param joinFetchPaths The join paths
* @return true if there are no "many" joins
*/
protected boolean isOnlySingleEndedJoins(RuntimePersistentEntity<?> rootPersistentEntity, Set<JoinPath> joinFetchPaths) {
boolean onlySingleEndedJoins = joinFetchPaths.isEmpty() || joinFetchPaths.stream()
.flatMap(jp -> {
PersistentPropertyPath propertyPath = rootPersistentEntity.getPropertyPath(jp.getPath());
if (propertyPath == null) {
return Stream.empty();
}
if (propertyPath.getProperty() instanceof Association) {
return Stream.concat(propertyPath.getAssociations().stream(), Stream.of((Association) propertyPath.getProperty()));
}
return propertyPath.getAssociations().stream();
})
.allMatch(association -> association.getKind() == Relation.Kind.EMBEDDED || association.getKind().isSingleEnded());
return onlySingleEndedJoins;
}

private static List<Association> associated(List<Association> associations, Association association) {
if (associations == null) {
return Collections.singletonList(association);
Expand Down

0 comments on commit 3f4004f

Please sign in to comment.