mrgenco
mrgenco

Reputation: 348

Generic JPA repository for runtime generated entities

In my scenario im generating hibernate entity classes at runtime under "com.mrg.domain" package. And in my generic restcontroller i can create instances of these entities according to @PathVariable. Below code works fine for this..

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

    Object model = null;    
    ObjectMapper mapper = new ObjectMapper();

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

        // Creating a new instance according to modelName
        Class<?> clazz = Class.forName("com.mrg.domain." + modelName);
        model = clazz.newInstance();

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

    } catch(Exception ex){

        // TODO handle exceptions & inform user..
    }   

    return new RestResponse(model.toString());
}

Now the next step i am trying to implement is a generic jpa repository(something like below) so that i can persist runtime generated models without implementing repositories for each entity. But couldn't find a solution yet.

@Repository
public interface GenericRepository<T> extends PagingAndSortingRepository<T, Long>{  }

Below topic and many other topics implemented generic repositories but also repositories per entities that uses generic repo. Since i have runtime generated entities repo implementation per entity doesnt work for me..

How to make generic jpa repository? Should I do this? Why?

Any suggestion or a way for achieving this? I'm new to generics and reflection so if what im trying to accomplish is not possible, tell me reasons and i would be appreciate..

Thanks and regards,

Upvotes: 0

Views: 1184

Answers (2)

jfk
jfk

Reputation: 5297

I had a react application where different data is defined in JSON and in the server side, I need to store this in the DB. My initial approach was to create entities , repositories and controller for all of this seperately. But another possible approach for CRUD operation is with MongoDB & JPA. Here is the idea.

import java.util.List;

import org.bson.Document;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/generic")
@CrossOrigin(origins = { "*" })
public class GenericController {

    @Autowired
    private MongoTemplate mongoTemplate;

    @PostMapping
    public ResponseEntity<Document> addData(@RequestBody String data) {
        JSONObject jsonObject = new JSONObject(data);
        String documentName = jsonObject.getString("documentName");
        Document doc = Document.parse(data);
        Document insertedDoc = mongoTemplate.insert(doc, documentName);
        return new ResponseEntity<>(insertedDoc, HttpStatus.CREATED);

    }

    @GetMapping("/{documentName}")
    public List<Document> getData(@PathVariable String documentName) {
        List<Document> allData = mongoTemplate.findAll(Document.class, documentName);
        return allData;
    }

}

Upvotes: 1

Coen Damen
Coen Damen

Reputation: 2109

You could use this pattern. This one uses EJB but can be used in Spring etc.

@Stateless
public abstract class AbstractRepository<T> {

@PersistenceContext
protected EntityManager em;

public abstract Class<T> getActualClass();

public T getSingleResult(Map<String, String> params) {

    // build querytext based on params
    TypedQuery<T> query = em.createQuery(queryText.toString(), getActualClass());
    ............
}
}

Now for the implementation class:

@Stateless
public class InputStreamRepository extends AbstractRepository<InputDataStream> {

@Override
public Class<InputDataStream> getActualClass() {
    return InputDataStream.class;
}

}

The getActualClass method will give you the Entity's class impl info.

Upvotes: 1

Related Questions