micronaut-projects/micronaut-data

Schema Generate doesn't work

Soromeister opened this issue · 3 comments

Expected Behavior

Schema generation should populate the DB with the expected tables derived from the declared entities.

Actual Behaviour

Nothing gets populated in the DB

Steps To Reproduce

  1. Main Application class:
import io.micronaut.configuration.picocli.PicocliRunner;
import io.micronaut.context.ApplicationContextBuilder;
import io.micronaut.context.ApplicationContextConfigurer;
import io.micronaut.context.annotation.ContextConfigurer;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.runtime.Micronaut;
import lombok.extern.slf4j.Slf4j;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Slf4j
@Command(name = "testapp", description = "...", mixinStandardHelpOptions = true)
public class Application implements Runnable {
    @Option(names = {"-v", "--verbose"}, description = "...")
    boolean verbose;

    @Option(names = {"-s", "--server"}, description = "start web server")
    boolean server;

    @Option(names = {"-c", "--config"}, description = "load config file")
    boolean config;

    @ContextConfigurer
    public static class Configurer implements ApplicationContextConfigurer {
        @Override
        public void configure(@NonNull ApplicationContextBuilder builder) {
            builder.defaultEnvironments("dev");
        }
    }

    public static void main(String[] args) throws Exception {
        Micronaut.run(Application.class, args);
//        PicocliRunner.run(Application.class, args);
    }

    public void run() {
        // business logic here
        if (verbose) {
            log.info("Hi! This is a Micronaut CLI application.");
        }
        if (server) {
            Micronaut.run(Application.class);
        }
        if (config) {
            parseConfigFile("config.properties");
        }
    }

    private void parseConfigFile(String filePath) {
        log.info("TODO: Implement config file parsing"); //TODO: Implement config file parsing
    }
}
  1. build.gradle:
//buildscript {
//    repositories {
//        mavenCentral()
//    }
//    dependencies {
//        classpath 'org.liquibase:liquibase-core:4.29.2'
//    }
//}

plugins {
    id 'idea'
    id 'java'
    id 'groovy'
//    id 'org.liquibase.gradle' version '3.0.1'
    id 'io.freefair.lombok' version '8.10.2'
    id 'com.github.johnrengelman.shadow' version "8.1.1"
    id 'io.micronaut.application' version "4.4.2"
    id 'io.micronaut.test-resources' version "4.4.2"
    id 'io.micronaut.aot' version "4.4.2"
}

version = "0.1"
group = "com.example"

idea {
    module {
        downloadJavadoc = true
        downloadSources = true
    }
}
repositories {
    mavenCentral()
}

application {
    mainClass = "com.example.Application"
}

java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

dependencies {
    annotationProcessor 'org.projectlombok:lombok'
    annotationProcessor 'io.micronaut.data:micronaut-data-processor'
    annotationProcessor 'io.micronaut:micronaut-http-validation'
    annotationProcessor 'io.micronaut.eclipsestore:micronaut-eclipsestore-annotations'
    annotationProcessor 'io.micronaut.serde:micronaut-serde-processor'
    annotationProcessor 'io.micronaut.spring:micronaut-spring-annotation'
    annotationProcessor 'io.micronaut.spring:micronaut-spring-boot-annotation'
    annotationProcessor 'io.micronaut.spring:micronaut-spring-web-annotation'
    annotationProcessor 'io.micronaut.validation:micronaut-validation-processor'
    annotationProcessor 'info.picocli:picocli-codegen'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.2'
    annotationProcessor 'io.github.linpeilie:mapstruct-plus-processor:1.4.5'
    annotationProcessor "io.micronaut:micronaut-inject-java"
    implementation 'info.picocli:picocli'
    implementation 'io.micronaut.picocli:micronaut-picocli'
    implementation 'io.micronaut:micronaut-http-server'
    implementation 'io.micronaut:micronaut-jackson-databind'
    implementation 'io.micronaut:micronaut-management'
    implementation 'io.micronaut:micronaut-websocket'
    implementation 'io.micronaut.data:micronaut-data-hibernate-jpa'
    implementation 'io.micronaut.data:micronaut-data-spring'
    implementation 'io.micronaut.data:micronaut-data-spring-jpa'
    implementation 'io.micronaut.eclipsestore:micronaut-eclipsestore'
    implementation 'io.micronaut.eclipsestore:micronaut-eclipsestore-annotations'
    implementation 'io.micronaut.liquibase:micronaut-liquibase'
    implementation 'io.micronaut.multitenancy:micronaut-multitenancy'
    implementation 'io.micronaut.problem:micronaut-problem-json'
    implementation 'io.micronaut.serde:micronaut-serde-jackson'
    implementation 'io.micronaut.session:micronaut-session'
    implementation 'io.micronaut.validation:micronaut-validation'
    implementation 'jakarta.annotation:jakarta.annotation-api'
    implementation 'jakarta.validation:jakarta.validation-api'
    implementation 'io.micronaut.sql:micronaut-jdbc-hikari'
    implementation 'org.springframework:spring-orm'
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mapstruct:mapstruct:1.6.2'
    implementation 'io.github.linpeilie:mapstruct-plus:1.4.5'
    implementation 'io.github.linpeilie:mapstruct-plus-spring-boot-starter:1.4.5'
    implementation 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
    compileOnly 'org.projectlombok:lombok'
    compileOnly 'io.micronaut:micronaut-http-client'
    runtimeOnly 'io.micronaut.spring:micronaut-spring-boot'
    runtimeOnly 'io.micronaut.spring:micronaut-spring-web'
//    runtimeOnly 'org.liquibase.ext:liquibase-hibernate5:4.27.0'
    runtimeOnly 'org.postgresql:postgresql'
    runtimeOnly 'org.slf4j:slf4j-simple'
    runtimeOnly 'org.yaml:snakeyaml'
    testAnnotationProcessor 'io.micronaut.spring:micronaut-spring-boot-annotation'
    testAnnotationProcessor 'io.micronaut.spring:micronaut-spring-web-annotation'
    testImplementation 'io.micronaut:micronaut-http-client'
    testImplementation 'net.minidev:json-smart:2.5.1'
    developmentOnly 'io.micronaut.controlpanel:micronaut-control-panel-management'
    developmentOnly 'io.micronaut.controlpanel:micronaut-control-panel-ui'
//    liquibaseRuntime 'javax.xml.bind:jaxb-api:2.3.1'
//    liquibaseRuntime 'org.liquibase:liquibase-core:4.26.1'
//    liquibaseRuntime 'org.liquibase:liquibase-groovy-dsl:3.0.2'
//    liquibaseRuntime 'info.picocli:picocli:4.6.1'
//    liquibaseRuntime 'org.postgresql:postgresql:42.7.4'
//    liquibaseRuntime 'org.liquibase.ext:liquibase-hibernate5:3.6'
//    liquibaseRuntime sourceSets.main.output
}

graalvmNative.toolchainDetection = false

micronaut {
    runtime("netty")
    testRuntime("spock2")
    processing {
        incremental(true)
        annotations("com.example.*")
    }
    aot {
        // Please review carefully the optimizations enabled below
        // Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
        optimizeServiceLoading = false
        convertYamlToJava = false
        precomputeOperations = true
        cacheEnvironment = true
        optimizeClassLoading = true
        deduceEnvironment = true
        optimizeNetty = true
        replaceLogbackXml = false
    }
}

tasks.named("dockerfileNative") {
    jdkVersion = "21"
}
  1. application-dev.yml:
endpoints:
  all:
    enabled: true
    sensitive: false
  health:
    details-visible: ANONYMOUS
  loggers:
    write-sensitive: false
  1. application.yml:
micronaut:
  application:
    name: testapp
  server:
    port: 8080
    host: ${HOST_ADDR}
  session:
    http:
      cookie: true
      header: true
datasources:
  default:
    driver-class-name: org.postgresql.Driver
    db-type: postgres
    dialect: POSTGRES
    url: jdbc:postgresql://localhost:5432/postgres
    username: postgres
    password: ''
    schema-generate: CREATE_DROP
jpa:
  default:
    properties:
      jakarta:
        persistence:
          schema-generation:
            scripts:
              action: create
              create-source: metadata
              create-target: createFile.sql
              drop-target: dropFile.sql
      javax:
        persistence:
          schema-generation:
            scripts:
              action: create
              create-source: metadata
              create-target: createFile.sql
              drop-target: dropFile.sql
      hibernate:
        hbm2ddl:
          auto: create
#liquibase:
#  datasources:
#    default:
#      change-log: classpath:db/liquibase-changelog.xml
  1. Abstract core entity:
import io.micronaut.data.annotation.DateCreated;
import io.micronaut.data.annotation.DateUpdated;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime;
import org.hibernate.annotations.SoftDelete;

@MappedSuperclass
public abstract class CoreEntity {

    @DateCreated
    private LocalDateTime created_at;

    @DateUpdated
    private LocalDateTime updated_at;

    @SoftDelete
    private LocalDateTime deleted_at;
}
  1. Example entity:
import com.example.module.common.entity.CoreEntity;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.GeneratedValue.Type;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExampleEntity extends CoreEntity implements Serializable {

    @Id
    @GeneratedValue(Type.UUID)
    private Long id;

    @Column(nullable = false, unique = true)
    private String name;

    @Column(nullable = false)
    @ColumnDefault("false")
    private Boolean status;
}

  1. Example repository:
import com.example.module.profile_types.dto.ProfileTypesOutput;
import io.micronaut.data.annotation.Repository;
import io.micronaut.data.repository.CrudRepository;
import io.micronaut.data.repository.jpa.JpaSpecificationExecutor;

@Repository
public interface ExampleRepository extends CrudRepository<ExampleEntity, Long>, JpaSpecificationExecutor<ExampleEntity> {

    ExampleEntity findByName(String name);
}

At this point, there is no need for service or controller, as schema generation should already be done (at least this is the case with Spring Boot).

Application produces the following output:

02:53:37: Executing 'run --args="-s" -s'...

> Task :processResources UP-TO-DATE
> Task :generateTestResourcesEffectiveLombokConfig UP-TO-DATE
> Task :compileTestResourcesJava NO-SOURCE
> Task :compileTestResourcesGroovy NO-SOURCE
> Task :inspectRuntimeClasspath
> Task :processTestResourcesResources NO-SOURCE
> Task :testResourcesClasses UP-TO-DATE
[test-resources-service] 02:53:38.498 [main] INFO  i.m.c.DefaultApplicationContext$RuntimeConfiguredEnvironment - Established active environments: [test]
[test-resources-service] 02:53:38.503 [ForkJoinPool.commonPool-worker-5] INFO  i.m.t.e.TestResourcesResolverLoader - Loaded 2 test resources resolvers: io.micronaut.testresources.postgres.PostgreSQLTestResourceProvider, io.micronaut.testresources.testcontainers.GenericTestContainerProvider
[test-resources-service] 02:53:38.633 [pool-1-thread-1] INFO  o.t.d.DockerClientProviderStrategy - Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
[test-resources-service] 02:53:38.728 [pool-1-thread-1] INFO  o.t.d.DockerClientProviderStrategy - Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
[test-resources-service] 02:53:38.730 [pool-1-thread-1] INFO  o.testcontainers.DockerClientFactory - Docker host IP address is localhost
[test-resources-service] 02:53:38.741 [pool-1-thread-1] INFO  o.testcontainers.DockerClientFactory - Connected to docker: 
  Server Version: 27.3.1
  API Version: 1.47
  Operating System: OrbStack
  Total Memory: 7997 MB
[test-resources-service] 02:53:38.746 [pool-1-thread-1] INFO  o.testcontainers.images.PullPolicy - Image pull policy will be performed by: DefaultPullPolicy()
[test-resources-service] 02:53:38.746 [pool-1-thread-1] INFO  o.t.utility.ImageNameSubstitutor - Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
[test-resources-service] 02:53:38.756 [pool-1-thread-1] INFO  tc.testcontainers/ryuk:0.7.0 - Creating container for image: testcontainers/ryuk:0.7.0
[test-resources-service] 02:53:38.853 [pool-1-thread-1] INFO  tc.testcontainers/ryuk:0.7.0 - Container testcontainers/ryuk:0.7.0 is starting: 28605a694b0006382d03f9aa2b5537e808ea869830ff18440dc9e7e1c2217dbe
[test-resources-service] 02:53:39.024 [pool-1-thread-1] INFO  tc.testcontainers/ryuk:0.7.0 - Container testcontainers/ryuk:0.7.0 started in PT0.268288S
[test-resources-service] 02:53:39.027 [pool-1-thread-1] INFO  o.t.utility.RyukResourceReaper - Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
[test-resources-service] 02:53:39.027 [pool-1-thread-1] INFO  o.testcontainers.DockerClientFactory - Checking the system...
[test-resources-service] 02:53:39.028 [pool-1-thread-1] INFO  o.testcontainers.DockerClientFactory - ✔︎ Docker server version should be at least 1.6.0
[test-resources-service] 02:53:39.136 [scheduled-executor-thread-1] INFO  i.m.t.server.ExpiryManager - Test resources server will automatically be shutdown if it doesn't receive requests for 60 minutes
[test-resources-service] 02:53:39.153 [main] INFO  i.m.t.server.TestResourcesService - A Micronaut Test Resources server is listening on port 60205, started in 737ms
> Task :internalStartTestResourcesService
> Task :generateEffectiveLombokConfig
> Task :compileJava
> Task :compileGroovy NO-SOURCE
> Task :classes

> Task :run
 __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [org.slf4j.simple.SimpleServiceProvider@258e2e41]
SLF4J(W): Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@3d299e3]
SLF4J(W): See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J(I): Actual provider is of type [org.slf4j.simple.SimpleServiceProvider@258e2e41]
[main] INFO io.micronaut.context.DefaultApplicationContext$RuntimeConfiguredEnvironment - Established active environments: [dev]
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@7048f722
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
[main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 6.5.2.Final
[main] INFO org.hibernate.cache.internal.RegionFactoryInitiator - HHH000026: Second-level cache disabled
[main] INFO org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
[main] INFO io.micronaut.runtime.Micronaut - Startup completed in 1757ms. Server Running: http://127.0.0.1:8080

I have no idea why it's not generating.

Environment Information

M1 Mac
macOS 14.7
PostgreSQL 17.0

Example Application

No response

Version

4.6.3

Switching to PicocliRunner.run(Application.class, args); and starting server via --server argument will result in this output (still not populating DB with schema):

> Task :run
 __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [org.slf4j.simple.SimpleServiceProvider@258e2e41]
SLF4J(W): Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@3d299e3]
SLF4J(W): See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J(I): Actual provider is of type [org.slf4j.simple.SimpleServiceProvider@258e2e41]
[main] INFO io.micronaut.context.DefaultApplicationContext$RuntimeConfiguredEnvironment - Established active environments: [cli]
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@7728643a
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
[main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 6.5.2.Final
[main] INFO org.hibernate.cache.internal.RegionFactoryInitiator - HHH000026: Second-level cache disabled
[main] INFO org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
[main] INFO io.micronaut.context.DefaultApplicationContext$RuntimeConfiguredEnvironment - Established active environments: [dev]
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
[main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Added connection org.postgresql.jdbc.PgConnection@5c997de8
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
[main] INFO org.hibernate.cache.internal.RegionFactoryInitiator - HHH000026: Second-level cache disabled
[main] INFO org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
[main] INFO io.micronaut.runtime.Micronaut - Startup completed in 301ms. Server Running: http://127.0.0.1:8080
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.

I see that file createFile.sql gets created with this content

create table example_entity (status boolean default false not null, created_at timestamp(6), deleted_at timestamp(6), id bigint not null, updated_at timestamp(6), name varchar(255) not null unique, primary key (id));

Isn't it what is configured here

  default:
    properties:
      jakarta:
        persistence:
          schema-generation:
            scripts:
              action: create
              create-source: metadata
              create-target: createFile.sql
              drop-target: dropFile.sql

and I think it works as expected.