currently I have the Task to create a REST api onto an existing service layer in Spring.
This is the Setup:
#Entity
public class Example{
#Id
public Long id;
...
}
public interface ExampleRepository extends CrudRepository<Example, Long> {}
#Service
public class ExampleService{
#Autowired
private ExampleRepository repo;
public List<Example> findAll(){
//do some businesslogic
return repo.findAll();
}
}
#RestController
#RequestMapping("/exampleService/*")
public class ExampleController{
#Autowired
private ExampleService service;
#GetMapping
public List<Example>findAll(){
return service.findAll();
}
}
The controller is only boilerplate to me and I would really like to find a way to generate it automatically because we are talking about a lot of services and even more functions.
I know there is a way to expose the repositories as REST-Endpoints using spring-data-rest but that is not what I want. I want the services to be exposed as REST-Endpoints. Could you please give me a hint on how to do that?
You should write a Generic Rest Controller which then calls your inner services.
You can achieve this by using external resource file for storing the class details and Java Reflection API .
Related
I have intermediate skills in Springboot and can develop simple to medium complex applications but this one is throwing me off a little bit.
I have a #Service that extends Guava's AbstractIdleService and a #Controller that has instance of the service autowired.
In the service, I have a method, say performTransaction() that is annotated with #Transactional (ofcourse to let Spring take care of transactions) and it does create a proxy of the service class. But when I debug the application I can see that the proxy that is injected in the controller contains all super class (AbstractIdleService) class fields as null meaning super() is not called when the proxy of my service was created. Because of this the application is failing to initialize when calling the super class's (AbstractIdleService) startAsync method. I am not really sure how to make this work.
I have added some code below for illustration.
This is my service class:
import com.google.common.util.concurrent.AbstractIdleService;
import org.springframework.transaction.annotation.Transactional;
#Service
public class MyService extends AbstractIdleService {
#Transactional
public void performTransaction() {
}
}
This is the controller that uses the above service:
#Controller
public void MyController {
private MyService service;
private EurekaRegistration eurekaRegistration;
#Autowired
public MyController(MyService service, EurekaRegistration eurekaRegistration) {
this.service = service; // This is where the proxy contains all AbstractIdleService fields null
this.eurekaRegistration = eurekaRegistration;
}
#EventListener({ ApplicationPreparedEvent.class })
public void handleApplicationPreparedEvent() {
this.service.startAsync(); // This line throws NPE, as delegate in the parent is null
this.service.awaitRunning();
}
}
I must use AbstractIdleService as that is the organizational convention and I need to invoke some startup code in the startAsync method. But invoking startAsync method on the service is causing a NullPointerException as it uses a parent field that is null because the proxy initialization never called super.
I'm not 100% sure if this is the actual problem, but try something like this (untested):
Interface for proxy:
import com.google.common.util.concurrent.Service;
public interface MyService extends Service {
void performTransaction();
}
Implementation:
import com.google.common.util.concurrent.AbstractIdleService;
import org.springframework.transaction.annotation.Transactional;
#Service
public class MyServiceImpl extends AbstractIdleService implements MyService {
#Transactional
public void performTransaction() {
}
}
Controller, declare service interface, not implementation:
#Controller
public void MyController {
private MyService service;
private EurekaRegistration eurekaRegistration;
#Autowired
public MyControlle(MyService service, EurekaRegistration eurekaRegistration) {
this.service = service;
this.eurekaRegistration = eurekaRegistration;
}
}
Following are my code
#RestController
public class EmployeeController {
#Autowired
EmployeeService empService;
public EmployeeController (EmployeeService Impl empServiceImpl) {
super();
this.empService = empServiceImpl;
}
}
#Service
public interface EmployeeService {
public List<EmployeeDTO> getAllEmployeeDetails()
}
public class EmployeeServiceImpl {
public List<EmployeeDTO> getAllEmployeeDetails(){
//methods business logic and repo call goes here
}
}
When I start my server I am getting below error.
Parameter 1 of constructor in
com.app.in.controller.EmployeeController required a bean of type
'com.app.in.service.EmployeeServiceImpl' that could not be found
My understanding might be wrong. If I annotate the EmployeeSeriveImpl class also with #Service then it working.Is that is the correct way to do it ? My question is the service interface is annotated with #Service still why its implementation is also required to annotation. Please let me know if I miss something in that ? What is the standard method to solve this issue ?
You can get your dependency injected using a constructor. And #Autowired is optional in this case.
This is your example, but with a few corrections:
#RestController
public class EmployeeController {
// private final is a good practice. no need in #Autowire
private final EmployeeService empService;
// this constructor will be used to inject your dependency
// #Autowired is optional in this case, but you can put it here
public EmployeeController (EmployeeService empServiceImpl) {
this.empService = empServiceImpl;
}
}
I assume you have an interface EmployeeService and class EmployeeServiceImpl which implements that interface and is Spring Bean.
Something like this:
#Service
public class EmployeeServiceImpl implements EmployeeService {}
Why this #Service is needed? When you put this annotation on your class, Spring knows this is a bean that Spring should manage for you (container will create an instance of it and inject it wherever it is needed).
Check Spring docs to get more details about Dependency Injection.
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null.
Can't wire layers in Spring Boot | MyBatis application. The problem is probably happening when Service layer uses Mapper.
Controller method sample:
#Controller
#RequestMapping("demo")
public class MessageController {
#Autowired
private MessageService messageService;
#RequestMapping(value = "messages", method = RequestMethod.GET)
public String getMessages(ModelMap modelMap) {
modelMap.addAttribute(MESSAGE,
messageService.selectMessages());
return "messages";
}
Service class:
#Service
public class MessageService {
#Autowired // Not sure if I can use Autowired here.
private MessageMapper messageMapper;
public MessageService() {
}
public Collection<Message> selectMessages() { return
messageMapper.selectAll(); }
}
MyBatis Mapper:
#Mapper
public interface MessageMapper {
#Select("select * from message")
Collection<Message> selectAll();
}
UPDATE
It feels like I'm having some fundamental knowledge based mistake. Probably managing external libraries.
Here's maven pom.xml. Looks kind of overloaded, I faced a lot of errors managing different spring-boot packages. Starter for autoconfiguration included.
pom.xml
Here's the project structure:
UPDATE #2
I'm sure DB connection is working well, I'm able to track changes in MySQL Workbench while Spring Boot is executing schema.sql and data.sql. But somehow, MyBatis mapper methods throw NullPointerException and page proceeds with exit code 500. Seems like they can't connect.
MessageService isn't managed by spring.
You have to annotate the MessageService class with #Service annotation (also, after adding this annotation you can indeed use #Autowired inside the service class)
#Service
public class MessageService {
#Autowired
private MessageMapper messageMapper;
public Collection<Message> selectMessages() {
return messageMapper.selectAll();
}
}
and wire it to the controller with
#Autowired
private MessageService messageService
and use it in a method like this
#RequestMapping(value = "messages", method = RequestMethod.GET)
public String getMessages(ModelMap modelMap) {
modelMap.addAttribute(MESSAGE, messageService.selectMessages());
return "messages";
}
I have been trying to create a spring boot application. In my application I would like to add some custom methods to save the data instead of using the default save method.
My application entry point is something like this:
#Configuration
#ComponentScan
#EnableJpaRepositories(repositoryImplementationPostfix = "CustomImpl")
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
#PropertySource("application.properties")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have changed this line repositoryImplementationPostfix to even Impl but, it didn't work.
My CrudRepository
#RepositoryRestResource
public interface TaRepository extends CrudRepository<Ta, Integer> ,TestRepository{
List<Ta> findByName(#Param("name") String name);
}
My Custom Repository:
public interface TestRepository {
public void myCustomMethod(TestDto dto);
}
My Custom Repository Impl
public class TestRepositoryCustomImpl implements TestRepository{
#PersistenceContext
private EntityManager em;
#Override
public void myCustomMethod(TestDto model){
}
NOTE:
If I change my CrudRepostory from the mentioned to this:
#RepositoryRestResource
public interface TaRepository extends CrudRepository<Ta, Integer> {
List<Ta> findByName(#Param("name") String name);
}
everything works fine. But not with the custom method implementation.
For Spring Data JPA #Repository or #RepositoryRestResource you never need to implement a Custom Interface. For any simple query you can create any kind of method, please follow the simple guide.
http://docs.spring.io/spring-data/jpa/docs/1.4.1.RELEASE/reference/html/jpa.repositories.html
For a complex query you can use JpaSpecificationExecutor.
How can I create a Predicate from a HQL query?
package com.lhoussaine.springjsfjpa.entities;
#Table(name="address")
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String streetNumber;
private String streetName;
private String city;
getter/setter
}
and I Have 30 entities.
Now repositories:
package com.lhoussaine.springjsfjpa.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.repository.annotation.RestResource;
import com.lhoussaine.springjsfjpa.entities.Address;
#RestResource(rel="address", path="address")
public interface AddressRepository extends JpaRepository<Address,Integer> {
}
Here I dont need to implemente CRUD operation! thanks to spring-data-jpa! And I want same standard for controller and services:
public interface IAddressService {
}
package com.lhoussaine.springjsfjpa.services.generic;
import java.util.List;
public abstract class GenericService<T,K> {
public abstract T create(T saved);
public abstract void remove(T deleted);
public abstract T findById(K id) ;
public abstract List<T> findAll();
public abstract T removeById(K id);
}
package com.lhoussaine.springjsfjpa.services.impl;
#Service
#Transactional
public class AddressService extends GenericService<Address, Integer> implements IAddressService {
#Autowired private AddressRepository iaddressRepository;
public Address create(Address saved) {
Address address=saved;
return iaddressRepository.save(address);
}
public void remove(Address deleted) {
iaddressRepository.delete(deleted);
}
public Address findById(Integer id) {
return iaddressRepository.findOne(id);
}
public List<Address> findAll() {
return iaddressRepository.findAll();
}
public Address removeById(Integer id) {
Address addr= iaddressRepository.findOne(id);
if(addr!=null){
iaddressRepository.delete(addr);
}
return addr;
}
}
Now the question is: with controller how I do?
Develop a controller for each class? knowing that I have 30 service classes.
Is there something approaching the same standard such as Spring Data JPA but for services and controller?
As you see with services classes! I'm obliged to make GenericService classes and create an interface for each class that I have in my package entities.
The controllers and the services should not be generic. Although it's understandable that every entity in your app could be created or found by ID, the services should only have the methods needed to implement the business logic of the app.
And the controllers should be created to implement the UI layer of your app. So, once you have a specification (or a clear idea in mind) of how a specific page of your application should look like and work, then implement te controller and the services to implement this page.
If your app is so generic that all it does is create, update and delete rows in tables, then you don't need to implement anything: a generic database web interface like PHPMyAdmin will do.
You can use generic service and controllers only without annotations, i.e with XML configuration. For controllers you also have to set Map<[methodName],org.springframework.web.bind.annotation.RequestMapping> for each controller and extend (override) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping to use it.
See example https://sites.google.com/site/beigeerp/home/spring-generic-controller