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

Allow limited no-id entity operations #1005

Merged
merged 1 commit into from
May 4, 2021
Merged
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 @@ -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,89 @@
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 io.micronaut.transaction.SynchronousTransactionManager
import io.micronaut.transaction.TransactionCallback
import io.micronaut.transaction.TransactionStatus
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import javax.inject.Inject

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

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

@Shared
@Inject
SynchronousTransactionManager transactionManager = applicationContext.getBean(SynchronousTransactionManager)

void setup() {
transactionManager.executeWrite(new TransactionCallback() {
@Override
Object call(TransactionStatus status) throws Exception {
status.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() }
return null
}

})
}

void 'test no-id entity operations'() {
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 @@ -89,7 +89,6 @@
import java.io.Serializable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
Expand Down Expand Up @@ -662,9 +661,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 @@ -17,7 +17,6 @@

import io.micronaut.core.annotation.NonNull;
import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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 @@ -106,7 +107,7 @@
* @author Denis Stepanov
* @since 1.0.0
*/
@SuppressWarnings("unchecked")
@SuppressWarnings("FileLength")
@Internal
public abstract class AbstractSqlRepositoryOperations<Cnt, RS, PS, Exc extends Exception> {
protected static final Logger QUERY_LOG = DataSettings.QUERY_LOG;
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 Expand Up @@ -1804,6 +1828,11 @@ protected SqlOperation(String query, Dialect dialect) {
this.dialect = dialect;
}

/**
* Expanded query.
*
* @return expanded query
*/
public String exandedQuery() {
return query;
}
Expand Down