micronaut-projects / micronaut-data

Ahead of Time Data Repositories
Apache License 2.0
465 stars 197 forks source link

[Bug] Can't use micronaut-data-jdbc with @PostConstruct and eagerInitSingletons #973

Closed altro3 closed 3 years ago

altro3 commented 3 years ago

Thanks for reporting an issue, please review the task list below before submitting the issue. Your issue report will be closed if the issue is incomplete and the below tasks not completed.

NOTE: If you are unsure about something and the issue is more of a question a better place to ask questions is on Stack Overflow (https://stackoverflow.com/tags/micronaut) or Gitter (https://gitter.im/micronautfw/). DO NOT use the issue tracker to ask questions.

Task List

I decided to play with the micronaut's settings and try different modes in my small service. There was a problem with the selection eagerInitSingletons mode.

In one of my service classes, there is data loading from the database after the bean is created:

public class SimpleService {

    @PostConstruct
    public void init() {
        loadFromDb();
        checkState();
    }
}

My Application:

        Micronaut.build(args)
                .classes(Application.class)
                .banner(false)
                .eagerInitSingletons(true)
                .start();

Ok, when I try to start application I get the exception:

Caused by: io.micronaut.data.exceptions.DataAccessException: Cannot convert type [class java.sql.Timestamp] with value [2021-04-12 14:58:10.85808] to target type: LocalDateTime updateTime. Consider defining a TypeConverter bean to handle this case.
    at io.micronaut.data.runtime.mapper.ResultReader.lambda$convertRequired$1(ResultReader.java:68)
    at java.base/java.util.Optional.orElseThrow(Optional.java:408)
    at io.micronaut.data.runtime.mapper.ResultReader.convertRequired(ResultReader.java:67)
    at io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper.convertAndSet(SqlResultEntityTypeMapper.java:648)
    at io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper.getJoins(SqlResultEntityTypeMapper.java:483)
    at io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper.readEntity(SqlResultEntityTypeMapper.java:359)
    at io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper.map(SqlResultEntityTypeMapper.java:168)
    at io.micronaut.data.jdbc.operations.DefaultJdbcRepositoryOperations$2.tryAdvance(DefaultJdbcRepositoryOperations.java:379)
    at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:326)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at io.micronaut.data.jdbc.operations.DefaultJdbcRepositoryOperations.lambda$findAll$7(DefaultJdbcRepositoryOperations.java:452)
    at io.micronaut.transaction.support.AbstractSynchronousTransactionManager.executeRead(AbstractSynchronousTransactionManager.java:155)
    at io.micronaut.data.jdbc.operations.DefaultJdbcRepositoryOperations.findAll(DefaultJdbcRepositoryOperations.java:450)
    at io.micronaut.data.runtime.intercept.DefaultFindAllInterceptor.intercept(DefaultFindAllInterceptor.java:52)
    at io.micronaut.data.runtime.intercept.DefaultFindAllInterceptor.intercept(DefaultFindAllInterceptor.java:36)
    at io.micronaut.data.intercept.DataIntroductionAdvice.intercept(DataIntroductionAdvice.java:80)
    at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:96)
    at io.micronaut.validation.ValidatingInterceptor.intercept(ValidatingInterceptor.java:137)
    at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:96)

I want to say that this error does not exist with the lazy mode of creating beans.

Environment Information

Example Application

dstepanov commented 3 years ago

That's probably because SimpleService init run before DataInitializer

altro3 commented 3 years ago

@dstepanov Hi!

Yes, logically I understand what the problem is, but I don't understand such a strange behavior of the system. See, I am creating a service bean that links to a repository bean. Accordingly, when the init method (@PostConstruct) is called, I expect to see that my bean is completely ready for use, including all the beans it refers to (at least this is the behavior of the spring). And now, it turns out that I cannot use the @PostConstruct annotation at all, because there is no guarantee that all dependencies are already initialized and ready to go.

For clarity, I will write out a little more basic dependency structure:

Service:

@Singleton
@RequiredArgsConstructor
public class SimpleService {

    private final SimpleRepo simpleRepo;

    @PostConstruct
    public void init() {
        simpleRepo.findAll();
    }
}

Repository:

@JdbcRepository(dialect = Dialect.POSTGRES)
public interface SimpleRepo extends CrudRepository<SimpleEntity, Integer> {

}

Entity:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
@MappedEntity
public class SimpleEntity {

    @Id
    @GeneratedValue(value = GeneratedValue.Type.SEQUENCE, ref = "simple_entity_id_seq")
    private Integer id;
    private String data;
    private LocalDateTime updateTime;
}
graemerocher commented 3 years ago

I agree this should fixed. As a workaround you can probably use an event listener instead of @PostConstruct:

 @EventListener
    public void init(StartupEvent event) {
        simpleRepo.findAll();
    }
altro3 commented 3 years ago

@graemerocher thank you! Yes, I know that option. The difference is that if I use this construction with lazy beans, then my bean is always (!) Constructed at the start and makes a request to the database at the start, i.e. the logic of work changes (I do not want to make a request at the start always if I have selected lazy initialization). It turns out that I cannot use the same code for lazy and eager options.

As I said, I decided to experiment with the settings. now I am "probing" the micronaut in order to transfer the system to its use. I try various cases using one of the simple services as an example.