如何在Spring Boot启动前以编程方式生成jOOQ POJO类?
问题描述
我通过以下代码用jOOQ从数据库生成POJO和记录类:
@Component public class JooqCodeGenerator implements CommandLineRunner { private static final Logger logger = LoggerFactory.getLogger(JooqCodeGenerator.class); @Value("${spring.datasource.url}") private String url; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Value("${jooq.generator.database.inputSchema}") private String inputSchema; @Value("${jooq.generator.target.packageName}") private String packageName; @Value("${jooq.generator.target.directory}") private String directory; @Override public void run(String... args) throws Exception { logger.info("Starting jOOQ code generation..."); Configuration configuration = new Configuration() .withJdbc(new Jdbc() .withDriver("org.postgresql.Driver") .withUrl(url) .withUser(username) .withPassword(password)) .withGenerator(new Generator() .withDatabase(new Database() .withInputSchema(inputSchema) .withIncludes(".*") // Include all tables .withExcludes("flyway_schema_history|pg_*")) // Exclude Flyway and default PostgreSQL tables .withTarget(new Target() .withPackageName(packageName) .withDirectory(directory)) .withGenerate(new Generate() .withDaos(true) .withPojos(true) .withInterfaces(false) .withImmutablePojos(false) .withFluentSetters(true) .withJavaTimeTypes(true))); GenerationTool.generate(configuration); logger.info("jOOQ code generation completed."); } }
但在服务和控制器中使用这些生成的类时,运行mvn spring-boot:run会出现编译错误:
[INFO] BUILD FAILURE [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project control: Compilation failure: Compilation failure: [ERROR] ProjectPath/rest/AuditTrailController.java:[3,54] package projectPath.jooq.tables.pojos does not exist
我不想用Maven插件,请问如何让Spring Boot在运行应用前以编程方式生成这些类?
解决方案
问题根源是CommandLineRunner是在Spring应用启动阶段执行的,但Maven的编译阶段(compile)在启动之前就已经完成了,此时生成的类还不存在,导致编译失败。要解决这个问题,需要把代码生成逻辑移到Maven编译阶段之前执行,同时保持编程式的实现方式。
方案1:编写独立Java主类,用Maven exec插件在编译前执行
- 把代码生成逻辑从
CommandLineRunner中抽出来,写成独立的可执行主类:
public class JooqCodeGeneratorMain { private static final Logger logger = LoggerFactory.getLogger(JooqCodeGeneratorMain.class); public static void main(String[] args) throws Exception { // 读取配置文件 Properties props = new Properties(); try (InputStream is = JooqCodeGeneratorMain.class.getClassLoader().getResourceAsStream("application.properties")) { props.load(is); } String url = props.getProperty("spring.datasource.url"); String username = props.getProperty("spring.datasource.username"); String password = props.getProperty("spring.datasource.password"); String inputSchema = props.getProperty("jooq.generator.database.inputSchema"); String packageName = props.getProperty("jooq.generator.target.packageName"); String directory = props.getProperty("jooq.generator.target.directory"); logger.info("Starting jOOQ code generation..."); Configuration configuration = new Configuration() .withJdbc(new Jdbc() .withDriver("org.postgresql.Driver") .withUrl(url) .withUser(username) .withPassword(password)) .withGenerator(new Generator() .withDatabase(new Database() .withInputSchema(inputSchema) .withIncludes(".*") .withExcludes("flyway_schema_history|pg_*")) .withTarget(new Target() .withPackageName(packageName) .withDirectory(directory)) .withGenerate(new Generate() .withDaos(true) .withPojos(true) .withInterfaces(false) .withImmutablePojos(false) .withFluentSetters(true) .withJavaTimeTypes(true))); GenerationTool.generate(configuration); logger.info("jOOQ code generation completed."); } }
- 在
pom.xml中配置exec-maven-plugin,让它在compile阶段之前执行这个主类:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>generate-jooq-classes</id> <phase>generate-sources</phase> <goals> <goal>java</goal> </goals> <configuration> <mainClass>com.yourpackage.JooqCodeGeneratorMain</mainClass> </configuration> </execution> </executions> <dependencies> <!-- 引入所需依赖 --> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq-codegen</artifactId> <version>${jooq.version}</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>${postgresql.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>${spring-boot.version}</version> </dependency> </dependencies> </plugin> </plugins> </build>
这样Maven会在generate-sources阶段执行代码生成,生成的类会被后续的compile阶段识别到。
方案2:自定义Maven生命周期插件(进阶)
如果需要跨项目复用代码生成逻辑,可以编写自定义Maven插件:
- 创建新的Maven项目,继承
maven-plugin - 编写Mojo类,在
execute()方法中实现jOOQ代码生成逻辑(和之前的生成逻辑一致) - 在主项目的
pom.xml中引入该插件,绑定到generate-sources阶段
方案3:切换到Gradle构建(如果允许)
Gradle的构建生命周期更灵活,可直接在构建脚本中编写代码生成逻辑:
task generateJooqClasses { doLast { def props = new Properties() file("src/main/resources/application.properties").withInputStream { props.load(it) } def configuration = new org.jooq.meta.jaxb.Configuration() .withJdbc(new org.jooq.meta.jaxb.Jdbc() .withDriver("org.postgresql.Driver") .withUrl(props.getProperty("spring.datasource.url")) .withUser(props.getProperty("spring.datasource.username")) .withPassword(props.getProperty("spring.datasource.password"))) .withGenerator(new org.jooq.meta.jaxb.Generator() .withDatabase(new org.jooq.meta.jaxb.Database() .withInputSchema(props.getProperty("jooq.generator.database.inputSchema")) .withIncludes(".*") .withExcludes("flyway_schema_history|pg_*")) .withTarget(new org.jooq.meta.jaxb.Target() .withPackageName(props.getProperty("jooq.generator.target.packageName")) .withDirectory(props.getProperty("jooq.generator.target.directory"))) .withGenerate(new org.jooq.meta.jaxb.Generate() .withDaos(true) .withPojos(true) .withInterfaces(false) .withImmutablePojos(false) .withFluentSetters(true) .withJavaTimeTypes(true))) org.jooq.codegen.GenerationTool.generate(configuration) } } compileJava.dependsOn(generateJooqClasses)
Gradle会在编译Java代码前先执行generateJooqClasses任务,生成所需类。
内容的提问来源于stack exchange,提问作者Omar Fulla
相关产品推荐
相关产品推荐

