I'm building a microservice architecture with Spring Framework, Feign and Eureka.
Shop works as an API Gateway, realised with Zuul (no database)
Each microservice has its own h2 database.
The CustomerService can call cart mappings with Feign
Example: I have a "Shop" and want to create a customer with a cart in one method in the service itself.
How can I call methods in the ShopService, to manage the other Services without having relationships to the other services? Please let me know if you need code examples.
CustomerController.java:
#ComponentScan
#RestController
public class CustomerController {
final Customer2CartConnectorRequester customer2CartConnectorRequester;
final Customer2OrderConnectorRequester customer2OrderConnectorRequester;
#Autowired
private CustomerJpaRepository customerJpaRepository;
#Autowired
public CustomerController(Customer2CartConnectorRequester customer2CartConnectorRequester,
Customer2OrderConnectorRequester customer2OrderConnectorRequester) {
this.customer2CartConnectorRequester = customer2CartConnectorRequester;
this.customer2OrderConnectorRequester = customer2OrderConnectorRequester;
}
#GetMapping("/list")
#ResponseBody
public List<CustomerEntity> getCustomers() {
return customerJpaRepository.findAll();
}
#RequestMapping("/{customerId}")
#ResponseBody
public Optional<CustomerEntity> getCustomer(#PathVariable("customerId") int customerId) {
return customerJpaRepository.findById(customerId);
}
// Client to Server
// POST über z.B. Postman Client
#PostMapping("/customer")
public CustomerEntity addCustomer(#RequestBody CustomerEntity customerEntity) {
customerJpaRepository.save(customerEntity);
return customerEntity;
}
#DeleteMapping("customer/{customerId}")
public String deleteCustomer(#PathVariable int customerId) {
CustomerEntity a = customerJpaRepository.getOne(customerId);
customerJpaRepository.delete(a);
return "deleted";
}
#PutMapping("/customer")
public CustomerEntity updateCustomer(#RequestBody CustomerEntity customerEntity) {
customerJpaRepository.save(customerEntity);
return customerEntity;
}
/// CONNECTOR REQUESTER ///
#GetMapping("/cart")
public List<?> getCart() {
return customer2CartConnectorRequester.getCart();
}
#GetMapping("/orders")
public List<?> getOrders() {
return customer2OrderConnectorRequester.getOrders();
}
}
Update:
I tried using a POST-Method to send data via Postman Client like this, but it seems like there is still something wrong:
Shop2CustomerConnectorRequester:
#FeignClient("customermicroservice")
public interface Shop2CustomerConnectorRequester {
#RequestMapping(value = "/list", method = RequestMethod.POST)
public ResponseEntity<String> createCustomer(Map<String, ?> queryMap);
}
ShopController:
#ComponentScan
#RestController
public class ShopController {
final Shop2CustomerConnectorRequester shop2CustomerConnectorRequester;
#Autowired
private ShopJpaRepository shopJpaRepository;
#Autowired
public ShopController(Shop2CustomerConnectorRequester shop2CustomerConnectorRequester) {
this.shop2CustomerConnectorRequester = shop2CustomerConnectorRequester;
}
#GetMapping("/customer")
public ResponseEntity<String> createCustomer(Map<String, ?> queryMap) {
return shop2CustomerConnectorRequester.createCustomer(queryMap);
}
}
Related
I have some trouble to get the right config for jpa repository for elasticsearch.
The configuration is for aws elasticsearch
#Component
public class AmazonElasticSearchConnector implements AmazonElasticSearchClient {
#Value("${elasticsearch.endpoint}")
private String elasticSearchEndpoint;
#Bean
public RestHighLevelClient createClient() {
return createClient(elasticSearchEndpoint);
}
}
and then i have another config
#Configuration
public class ESConfig {
#Autowired
private RestHighLevelClient client;
#Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchRestTemplate(client);
}
}
Here my Entity
#Entity
#Document(indexName = "item")
public class SupplierItemFilterResponseEntity {
#ElementCollection
private List<String> searchCollect;
#Id
private int id;
....
and my nested Entity
#Entity
#Document(indexName = "item")
public class TechnologyFilterEntity {
public int getTechnologyID() {
return technologyID;
}
public void setTechnologyID(int technologyID) {
this.technologyID = technologyID;
}
....
and I have a repository
public interface SupplierItemFilterESRepository extends
ElasticsearchRepository<SupplierItemFilterResponseEntity, Integer> {
}
And then the service that encountered an error
#Autowired
private RestHighLevelClient client;
#Autowired
private SupplierItemFilterESRepository supplierItemFilterESRepository;
public Page<SupplierItemFilterResponseEntity> getSupplierWithGivenFiltersPagination(Pageable pageable) {
return supplierItemFilterESRepository.findAll(pageable);
}
....
and the Error i get is following
No property indexWithoutRefresh found for type SupplierItemFilterResponseEntity!
Im on it for 2 days now, and my expertise is over. Maybe someone has the same problem.
Cheers and thank you if you can help!
I am trying to post data from postman via my Spring Boot 2 Application with Spring Data JPA into a MySQL Database. All I get is a 404 Error.
Main
#SpringBootApplication
public class ProfileApplication {
public static void main(String[] args) {
SpringApplication.run(ProfileApplication.class, args);
}
}
Entity
#Entity
public #Data class Profile {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String profileText;
}
Controller
#RestController
#RequestMapping(value = "/profile", produces = { MediaType.APPLICATION_JSON_VALUE })
public class ProfileController {
#Autowired
private ProfileRepository profileRepository;
public ProfileRepository getRepository() {
return profileRepository;
}
#GetMapping("/profile/{id}")
Profile getProfileById(#PathVariable Long id) {
return profileRepository.findById(id).get();
}
#PostMapping("/profile")
Profile createOrSaveProfile(#RequestBody Profile newProfile) {
return profileRepository.save(newProfile);
}
}
Repository
public interface ProfileRepository extends CrudRepository<Profile, Long> {
}
application.propterties
server.port = 8080
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/profiledb
spring.datasource.username=root
spring.datasource.password=
server.servlet.context-path=/service
It seems that in your ProfileController, you have defined twice the profile endpoint (first at the class level, and second on the methods). The solution would be to remove one of them:
#RestController
#RequestMapping(value = "/profile", produces = { MediaType.APPLICATION_JSON_VALUE })
public class ProfileController {
#Autowired
private ProfileRepository profileRepository;
public ProfileRepository getRepository() {
return profileRepository;
}
// Notice that I've removed the 'profile' from here. It's enough to have it at class level
#GetMapping("/{id}")
Profile getProfileById(#PathVariable Long id) {
return profileRepository.findById(id).get();
}
// Notice that I've removed the 'profile' from here. It's enough to have it at class level
#PostMapping
Profile createOrSaveProfile(#RequestBody Profile newProfile) {
return profileRepository.save(newProfile);
}
}
Which url? Valid url must look like:
GET: http://localhost:8080/service/profile/profile/1
POST: http://localhost:8080/service/profile/profile
I have catalog service that works with product service to get data (microservices). When I try to make getForObject in catalog service, I have an error 404.
#RestController
#RequestMapping("/catalog")
public class ProductCatalogApi {
#Autowired
private RestTemplate restTemplate;
#GetMapping("")
public String hello(){
return "Heelloooo";
}
#GetMapping("/{category}")
public void getProductsByCategoryName(#PathVariable String category) {
UserProduct userProduct = restTemplate.getForObject(
"http://shop-product-service/shop/products" + category,
UserProduct.class);
System.out.println("dsdasa--------"+ userProduct);
}
This is my product service:
#RestController
#RequestMapping("/shop")
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/all")
public List<Product> index(){
return productRepository.findAll();
}
#GetMapping("/product/{id_product}")
public Optional<Product> showByProductId(#PathVariable String id_product){
return productRepository.findById(id_product);
}
#GetMapping("/products/{category}")
public List<Product> showByCategoryName(#PathVariable String category){
return productRepository.findByCategory(category);
}
}
So when I try to make link this: http://localhost:8082/catalog/electronics, I get error, Please help me.
you lost the character "/" in the class ProductCatalogApi :
restTemplate.getForObject("http://shop-product-service/shop/products" + category,
UserProduct.class);
http://shop-product-service/shop/products =>
http://shop-product-service/shop/products/
the problem is once i use method findAll using MongoOperation interface, it working fine with no exception but only return One Record, however the collection contain more than 1 record. i tried a different interface like mongotemp and also the same result, List size is return always 1, even when i try it from dao itself
Configuration Spring DATA with mongodb
#Configuration
public class MongodbConfig {
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "inSpace");
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
DAO Class
#Repository
public class GenericDAOImpl<T> implements GenericDAO<T> {
#Autowired
protected MongoOperations mongoOper;
public <T> void save(T ct) {
mongoOper.save(ct);
}
public <T> void remove(T ct) {
mongoOper.remove(ct);
}
public T getBydId(String id, Class<T> clazz) {
return mongoOper.findById(id, clazz);
}
public List<T> getAll(Class<T> clazz) {
System.err.println(" ''''''' " + mongoOper.findAll(clazz).size());
return mongoOper.findAll(clazz);
}
}
Note: the upper two classs in a single project and used as dependency for the other project who contain the below classes
Service calling DAO
#Service("userService")
public class UserServiceImpl implements UserService {
#Autowired
private GenericDAO<Users> userDAO;
public Users create(Users user) {
userDAO.save(user);
return user;
}
public Users delete(String id) {
Users user = userDAO.getBydId(id, Users.class);
userDAO.remove(user);
return user;
}
public List<Users> findAll() {
System.out.println("inside User Service");
return userDAO.getAll(Users.class);
}
public Users findById(String id) {
return userDAO.getBydId(id , Users.class);
}
}
Controller Class
#RestController
#RequestMapping("/user")
public class UsersController {
#Autowired
UserService userService;
#RequestMapping(method = RequestMethod.GET, value = "/allUsers")
List<Users> getAllRegistedUsers() {
System.out.println("inside UserController");
return userService.findAll();
}
}
Configuration Class
#SpringBootApplication
#ComponentScan(basePackageClasses=
{UsersController.class,UserService.class,GenericDAO.class})
public class UsersConfiguration {
public static void main(String[] args) {
SpringApplication.run(UsersConfiguration.class, args);
}
}
Note: when i try to inject bean in Main methods and use the bean by ApplicationContext it's work fine and return all records in database !?
I need such a usage:
For each request I want to inject userId into DemoController But because of being a final class without empty constructor I can not inject it. What is the best practice in such cases? A service with request scope is fine?
#Configuration
public class CityFactory{
#Bean(name = {"currentUserId")
#Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
#Autowired
public Integer getUserId(HttpServletRequest request) {
return UserUtil.getCurrentUserId(request.getServerName());
}
}
#RequestMapping("/demo")
#Controller
public class DemoController {
#Autowired
Ingeter userId;
#RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
public ModelAndView helloWorld(#PathVariable("name") String name, Model model) {
Map<String, Object> myModel = new HashMap<String, Object>();
model.addAttribute("user", userId);
return new ModelAndView("v3/test", "m", model);
}
}
Your best bet is to create an explicit class called UserId, which in turn contains an integer. Not only will this play nicer with CGLIB's proxying, it also clarifies your design.
You can use Supplier or Provider
#Configuration
public class CityFactory{
#Bean
#Autowired
public Supplier<Integer> getUserId(HttpServletRequest request) {
return () -> UserUtil.getCurrentUserId(request.getServerName());
}
}
#RequestMapping("/demo")
#Controller
public class DemoController {
#Autowired
Supplier<Ingeter> getUserId;