How to make controller endpoint to get two different objects in java spring?
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
@Controller
public class Controller{
@RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option1 data1){
return "option1"
}
@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
java json spring generic-programming
add a comment |
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
@Controller
public class Controller{
@RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option1 data1){
return "option1"
}
@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
java json spring generic-programming
@AndrewTobilko no they don't have anything in common, totally different objects with different fields
– JJ Redikes
Nov 20 at 15:51
@AndrewTobilko this is only example, I have other goal to do with that for example if I'm working with WebSocket I would like to send a few different objects on the same channel. its easy to explain with controller and endpoint than WebSocket from the reason that WebSocket and endpoint works almost the same
– JJ Redikes
Nov 20 at 16:01
@AndrewTobilko 'option1and2' can either option1 or option2
– JJ Redikes
Nov 22 at 10:32
it doesn't work that way, you need to define a certain type as a method param
– Andrew Tobilko
Nov 22 at 10:55
add a comment |
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
@Controller
public class Controller{
@RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option1 data1){
return "option1"
}
@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
java json spring generic-programming
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
@Controller
public class Controller{
@RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option1 data1){
return "option1"
}
@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
java json spring generic-programming
java json spring generic-programming
edited Nov 21 at 17:36
asked Nov 20 at 15:32
JJ Redikes
185
185
@AndrewTobilko no they don't have anything in common, totally different objects with different fields
– JJ Redikes
Nov 20 at 15:51
@AndrewTobilko this is only example, I have other goal to do with that for example if I'm working with WebSocket I would like to send a few different objects on the same channel. its easy to explain with controller and endpoint than WebSocket from the reason that WebSocket and endpoint works almost the same
– JJ Redikes
Nov 20 at 16:01
@AndrewTobilko 'option1and2' can either option1 or option2
– JJ Redikes
Nov 22 at 10:32
it doesn't work that way, you need to define a certain type as a method param
– Andrew Tobilko
Nov 22 at 10:55
add a comment |
@AndrewTobilko no they don't have anything in common, totally different objects with different fields
– JJ Redikes
Nov 20 at 15:51
@AndrewTobilko this is only example, I have other goal to do with that for example if I'm working with WebSocket I would like to send a few different objects on the same channel. its easy to explain with controller and endpoint than WebSocket from the reason that WebSocket and endpoint works almost the same
– JJ Redikes
Nov 20 at 16:01
@AndrewTobilko 'option1and2' can either option1 or option2
– JJ Redikes
Nov 22 at 10:32
it doesn't work that way, you need to define a certain type as a method param
– Andrew Tobilko
Nov 22 at 10:55
@AndrewTobilko no they don't have anything in common, totally different objects with different fields
– JJ Redikes
Nov 20 at 15:51
@AndrewTobilko no they don't have anything in common, totally different objects with different fields
– JJ Redikes
Nov 20 at 15:51
@AndrewTobilko this is only example, I have other goal to do with that for example if I'm working with WebSocket I would like to send a few different objects on the same channel. its easy to explain with controller and endpoint than WebSocket from the reason that WebSocket and endpoint works almost the same
– JJ Redikes
Nov 20 at 16:01
@AndrewTobilko this is only example, I have other goal to do with that for example if I'm working with WebSocket I would like to send a few different objects on the same channel. its easy to explain with controller and endpoint than WebSocket from the reason that WebSocket and endpoint works almost the same
– JJ Redikes
Nov 20 at 16:01
@AndrewTobilko 'option1and2' can either option1 or option2
– JJ Redikes
Nov 22 at 10:32
@AndrewTobilko 'option1and2' can either option1 or option2
– JJ Redikes
Nov 22 at 10:32
it doesn't work that way, you need to define a certain type as a method param
– Andrew Tobilko
Nov 22 at 10:55
it doesn't work that way, you need to define a certain type as a method param
– Andrew Tobilko
Nov 22 at 10:55
add a comment |
3 Answers
3
active
oldest
votes
You should use JsonNode object.
for your example you should do this:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
1
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
add a comment |
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
@Data
public class Option1And2{
private String name;
private Long id;
}
- If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule. - If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
add a comment |
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a @Service
or @Repository
, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with @RequestMapping
automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added @RequestMapping
annotation and then call the super class method.
Another approach is using annotations.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53396390%2fhow-to-make-controller-endpoint-to-get-two-different-objects-in-java-spring%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
You should use JsonNode object.
for your example you should do this:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
1
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
add a comment |
You should use JsonNode object.
for your example you should do this:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
1
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
add a comment |
You should use JsonNode object.
for your example you should do this:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
You should use JsonNode object.
for your example you should do this:
@Controller
public class Controller{
@RequestMapping(value = "service/getData", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
answered Nov 27 at 12:24
Tal Shani
829
829
1
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
add a comment |
1
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
1
1
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
thank you!!! that's exactly what I looked for
– JJ Redikes
Nov 27 at 13:06
add a comment |
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
@Data
public class Option1And2{
private String name;
private Long id;
}
- If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule. - If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
add a comment |
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
@Data
public class Option1And2{
private String name;
private Long id;
}
- If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule. - If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
add a comment |
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
@Data
public class Option1And2{
private String name;
private Long id;
}
- If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule. - If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
@Data
public class Option1And2{
private String name;
private Long id;
}
- If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule. - If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
edited Nov 21 at 6:15
answered Nov 21 at 6:06
AokoQin
794
794
add a comment |
add a comment |
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a @Service
or @Repository
, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with @RequestMapping
automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added @RequestMapping
annotation and then call the super class method.
Another approach is using annotations.
add a comment |
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a @Service
or @Repository
, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with @RequestMapping
automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added @RequestMapping
annotation and then call the super class method.
Another approach is using annotations.
add a comment |
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a @Service
or @Repository
, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with @RequestMapping
automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added @RequestMapping
annotation and then call the super class method.
Another approach is using annotations.
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a @Service
or @Repository
, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with @RequestMapping
automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added @RequestMapping
annotation and then call the super class method.
Another approach is using annotations.
edited Nov 21 at 17:31
answered Nov 21 at 6:34
Jabari Dash
1,112617
1,112617
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53396390%2fhow-to-make-controller-endpoint-to-get-two-different-objects-in-java-spring%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
@AndrewTobilko no they don't have anything in common, totally different objects with different fields
– JJ Redikes
Nov 20 at 15:51
@AndrewTobilko this is only example, I have other goal to do with that for example if I'm working with WebSocket I would like to send a few different objects on the same channel. its easy to explain with controller and endpoint than WebSocket from the reason that WebSocket and endpoint works almost the same
– JJ Redikes
Nov 20 at 16:01
@AndrewTobilko 'option1and2' can either option1 or option2
– JJ Redikes
Nov 22 at 10:32
it doesn't work that way, you need to define a certain type as a method param
– Andrew Tobilko
Nov 22 at 10:55