Search code examples
javaspringspring-boothibernatejpa

Create Spring JPA Entities at runtime


I would like to create JPA Entities at runtime, based on a given database scheme, that will be polled through a REST-API and I need to register them in the Spring Context.

There are two approaches that comes to my mind:

  1. Use a code generator, similiar to hbm2java (hibernate-tools), generate the entities at startup. However I have to restart everytime a table will be added.

  2. Generate code for the entity as well but compile the java code at runtime and register the entity in the spring context manually.

It has to be Spring Data JPA, because I want to setup Spring Data (or OData with Spring support) on top.

Is there an approach to register entities at runtime without creating a POJO/Entity class (as a file)?


Solution

  • You can use Bytebuddy to create classes at runtime and load them to a specific ClassLoader :

        Map<String, Class<?>> fields = new HashMap<>();
        fields.put("firstName", String.class);
        fields.put("lastName", String.class);
        fields.put("birthDate", Date.class);
        fields.put("image", byte[].class);
        fields.put("version", Long.class);
        var className = "com.hzerai.dynamicjpa.models.Person";
        var builder = new ByteBuddy().subclass(Object.class)
                .annotateType(AnnotationDescription.Builder.ofType(javax.persistence.Entity.class).build());
        for (Map.Entry<String, Class<?>> e : fields.entrySet()) {
            builder = builder.defineField(e.getKey(), e.getValue())
                    .annotateField(AnnotationDescription.Builder.ofType(javax.persistence.Column.class).build());
        }
        builder = builder.defineField("id", Long.class).annotateField(
                AnnotationDescription.Builder.ofType(javax.persistence.Id.class).build(),
                AnnotationDescription.Builder.ofType(javax.persistence.Column.class).build(),
                AnnotationDescription.Builder.ofType(javax.persistence.GeneratedValue.class).build());
        Unloaded<?> generatedClass = builder.name(className).make();
        generatedClass.load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
        Class<?> cls = Class.forName(className);
    

    You can do the same to the spring repositories.

    After loading the classes to the classLoader, you just need to refresh the spring context to rescan the packages.

    I did something similar creating entities and managing them using jpa/hibernate at runtime.

    Check-out this repo : https://github.com/HabibZerai/dynamic-jpa.git