mrgenco
mrgenco

Reputation: 348

Runtime generated repositories and entities

In my SpringBoot application i'm generating hibernate entity classes and repositories using javapoet and than compiling these generated source files respectively using OpenHFT library at runtime. My purpose is being able to persist these runtime generated entities.

I could successfully use this generated entity inside my rest controller and map @RequestBody json String to this entity. But my problem is i couldn't inject my runtime generated repository to the controller..

Here is an example runtime generated entity;

@Entity
public class Author extends BaseEntity{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;

    public Author(){
        super();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

Here is the runtime generated repository for above entity

import org.springframework.stereotype.Repository;
import java.lang.Long;
import com.mrg.domain.Author;

@Repository("authorRepository")
public interface AuthorRepository extends GenericRepository<Author, Long> {

}

Here is the generic repository i'm using so that i can inject my repos at runtime

@NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable  > extends PagingAndSortingRepository<T, ID>{

}

And below is my rest controller. Here im autowiring generic repository as a Map so that Spring is injecting correct repository implementation dynamically when i use it with repository name as a key;

genericRepo.get(repoName).save(model);

@RestController
@RequestMapping("/{entity}")
public class GenericRestController {


    @Autowired
    private Map<String, GenericRepository> genericRepo;

    @RequestMapping(value = "/{entity}/", method = RequestMethod.POST)
    public @ResponseBody Object createEntity(@PathVariable String entity, @RequestBody String requestBody) {

        Object model = null;
        ObjectMapper mapper = new ObjectMapper();
        String repoName = "";

        try {

            // ex : if {entitiy} param is equal "author" modelName will be "Post"
            String modelName = Character.toUpperCase(entity.charAt(0)) + entity.substring(1);

            Class<?> clazz = Class.forName("com.mrg.domain." + modelName);
            model = clazz.newInstance();

            // Converting @RequestBody json String to domain object..
            model = mapper.readValue(requestBody, clazz);

            // Repository name is {entity} + "Repository" ex : authorRepository
            repoName = entity.concat("Repository");

        } catch (Exception ex) {

            // handling exceptions..
        }
        // Saving with right repository 
        return genericRepo.get(repoName).save(model);
    }
}

This is working for repositories that i wrote manually and i can persisting objects with this approach dynamically. But i couldnt access my runtime generated repositories.(genericRepo.get("authorRepository") is returning null reference)

Could you suggest a solution for this problem. What am i missing here? Any other idea for persisting runtime generated objects would be helpfull.

Thanks..

Upvotes: 3

Views: 3101

Answers (2)

gtBacteria
gtBacteria

Reputation: 21

Recently, I faced with similar scenario, having to generate Repository at runtime to reduce boilerplate code. Through some research, it turns out that when scanning for interfaces annotated with @Repository, it's using ClassPathScanningCandidateComponentProvider, which scans for classes in classpath, ignoring runtime-generated classes.

Upvotes: 1

dimirsen Z
dimirsen Z

Reputation: 891

Your repositories live in a package, for example com.mypackage.repository. In your config you should scan this package (and the package with controllers as well) so that Spring becomes aware of these beans:

@Configuration
@ComponentScan(value = {"com.mypackage.controller"})
@EnableJpaRepositories(basePackages = {"com.mypackage.repository"})
public class MyConfig {
    // creation and configuration of other beans
}

Config class may have more annotations for you own purposes (say, @EnableTransactionManagement, @PropertySource for props in text files). I just post the obligatory set of annotations.

Upvotes: 0

Related Questions