Spring boot different in hibernate vs eclipselink - spring

I'm trying to change from Hibernate to Eclipselink as Jpa provider for spring boot. However, I'm seeing some difference in behaviour between the two.
Eclipselink Configuration
package com.test.test2;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
public class EclipselinkJpaConfiguration extends JpaBaseConfiguration {
protected EclipselinkJpaConfiguration(DataSource dataSource, JpaProperties
properties, ObjectProvider<JtaTransactionManager> jtaTransactionManager) {
super(dataSource, properties, jtaTransactionManager);
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new EclipseLinkJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> map = new HashMap<String, Object>();
map.put(PersistenceUnitProperties.WEAVING, "false");
return map;
}
}
Controller
package com.test.test2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
#RestController
public class PersonController {
#Autowired
PersonService personService;
#GetMapping("/test")
public ResponseEntity<String> test() {
return new ResponseEntity<String>(personService.helloWorld(), HttpStatus.OK);
}
#PostMapping("/createNewPerson")
public ResponseEntity<Person> createNewPerson() {
Person res = personService.createNewPerson();
return new ResponseEntity<Person>(res, HttpStatus.OK);
}
#PostMapping("/updatePerson/{id}")
public ResponseEntity<Person> updatePerson(#PathVariable("id") Long id) {
Person res = personService.updatePerson(id);
personService.transAnnotation();
return new ResponseEntity<Person>(res, HttpStatus.OK);
}
}
Service
package com.test.test2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
#Service
public class PersonService {
#Autowired
PersonRepository personRepository;
public String helloWorld() {
return "Hello World!";
}
public Person createNewPerson () {
Person newPerson = new Person();
newPerson.setFirstName("firstName");
newPerson.setLastName("lastName");
return personRepository.save(newPerson);
}
public Person updatePerson(Long id) {
Optional<Person> personOP = personRepository.findById(id);
if (personOP.isPresent()) {
Person person = personOP.get();
person.setFirstName("Update Name");
}
return null;
}
#Transactional(readOnly = true)
public void transAnnotation () {
}
}
Repository
package com.test.test2;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface PersonRepository extends JpaRepository<Person, Long>{
}
Eclipselink behavior
When calling /updatePerson/{id} api, person.firstName is updated to db without calling personRepoitory.save() if the next method is tagged with #Transactional.
As spring repository itself is transactional, even calling a personRepository.findAll() will commit and firstName is updated to db.
Logs
2021-11-24 12:01:06.264 INFO 2868 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path ''
2021-11-24 12:01:06.269 INFO 2868 --- [ main] com.test.test2.Test2Application : Started Test2Application in 1.966 seconds (JVM running for 2.227)
2021-11-24 12:01:12.689 INFO 2868 --- [nio-8081-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-11-24 12:01:12.689 INFO 2868 --- [nio-8081-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-11-24 12:01:12.689 INFO 2868 --- [nio-8081-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
[EL Fine]: sql: 2021-11-24 12:01:12.741--ServerSession(2122837918)--Connection(555991754)--SELECT ID, first_name, last_name FROM TBL_PERSON WHERE (ID = ?)
bind => [1]
[EL Fine]: sql: 2021-11-24 12:01:12.771--ClientSession(1084235622)--Connection(819605412)--UPDATE TBL_PERSON SET first_name = ? WHERE (ID = ?)
bind => [Update Name, 1]
Whereas for Hibernate, the changes are not commited to db unless a save() is called.
I am aware that a if a function is tagged with #Transactional, we do not need to call save() as it will auto commit the changes. However, currently it is tagged only in the next method called.
Any possible causes for this? Could it be due to incorrect configuration?

Related

Getting 500 error while testing webflux code with wiremock

I have spring webflux app with user controller class with end point "/user and user service class.The user service class making call to external api. I am trying to test the service class using wiremock and junit 5 to mock out external api.. However I am getting below error ->
021-07-30 18:22:52.511 ERROR 16974 --- [o-auto-1-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
java.net.ConnectException: Connection refused
Code is uploaded at path : https://github.com/neeleshsethi/wiremockdemp/tree/master
It seems it cannot find controller as adding a print statement in controller is not printing anything. Below is the code ->
#Service
public class UserService {
#Autowired
WebClient webClient;
public Mono<User> createuser(User user) {
return webClient.post()
.uri("/usercreate")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(user))
.retrieve()
.bodyToMono(User.class);
}
}
WireMock Inti class:
package com.example.wiremockdemo;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import java.util.Map;
public class WireMockInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
WireMockServer wireMockServer = new WireMockServer(new WireMockConfiguration().dynamicPort());
wireMockServer.start();
applicationContext.addApplicationListener( applicationEvent ->
{
if(applicationEvent instanceof ContextClosedEvent)
{
wireMockServer.stop();
}
}
);
applicationContext.getBeanFactory().registerSingleton("wireMockServer", wireMockServer);
TestPropertyValues.of("externalBaseUrl",wireMockServer.baseUrl())
.applyTo(applicationContext);
}
}
test class:
package com.example.wiremockdemo;
import com.example.wiremockdemo.model.User;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;
import reactor.core.publisher.Mono;
import java.awt.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(initializers = {WireMockInit.class})
class WiremockdemoApplicationTests {
#Autowired
WebTestClient webTestClient;
#Autowired
private WireMockServer wireMockServer;
#LocalServerPort
private Integer port;
#Test
void createUsertest() {
System.out.println("Creating stub");
wireMockServer.stubFor(
WireMock.post("/usercreate")
.willReturn(
aResponse()
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBodyFile("response.json"))
);
byte[] temp = webTestClient.post()
.uri("http://localhost:" + port + "/user")
// .uri("/user")
.contentType(MediaType.APPLICATION_JSON)
//.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(createUser()))
.exchange()
.expectBody()
.returnResult()
.getResponseBody();
String s = new String(temp);
System.out.println("Response :" +s);
}
public User createUser()
{
return User.builder()
.firstName("neel")
.age(32)
.id(1234)
.build();
}
}
Change this line to:
TestPropertyValues.of(Map.of("externalBaseUrl", wireMockServer.baseUrl()));
This is the correct way to set test properties.

Axon Event Sourcing not generating table

I am learning how to use the axon framework for event sourcing, seems to be going good but I have gotten stuck on the database configuration for event sourcing. From what I understood from docs / other articles, the database table should automatically generate.
I first tried with H2, the database table was generated by itself, everything working fine.
I added my own mysql db, and the database table is not being created.. I'm getting the error
'Table 'producttest.domain_event_entry' doesn't exist
I was under the impression that the table will generate itself, I must be doing something wrong here, but I'm unsure what. Could anyone help me please?
My code:
ProductAggregate class
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.spring.stereotype.Aggregate;
import java.util.UUID;
import static org.axonframework.modelling.command.AggregateLifecycle.apply;
#NoArgsConstructor
#Aggregate
#Slf4j
public class ProductAggregate {
#AggregateIdentifier
private String productId;
private String productName;
private String productDescription;
#CommandHandler
public ProductAggregate(ProductCreateCommand command){
log.info("handling {}", command);
apply(new ProductCreatedEvent(command.getProductId(),command.getProductName(),command.getProductDescription()));
}
#EventSourcingHandler
public void onProductCreateEvent(ProductCreatedEvent event){
log.info("applying {}", event);
this.productId = event.getProductId();
this.productName = event.getProductName();
this.productDescription = event.getProductDescription();
}
}
ProductCreateCommand
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class ProductCreateCommand {
#TargetAggregateIdentifier
private String productId;
private String productName;
private String productDescription;
}
ProductCreatedEvent
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class ProductCreatedEvent {
#TargetAggregateIdentifier
private String productId;
private String productName;
private String productDescription;
}
TestRunner
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.UUID;
#Component
#Slf4j
public class TestRunner implements CommandLineRunner {
private final CommandGateway commandGateway;
#Autowired
public TestRunner(CommandGateway commandGateway) {
this.commandGateway = commandGateway;
}
#Override
public void run(String... args) throws Exception {
log.info("sending product create command");
commandGateway.sendAndWait(new ProductCreateCommand(UUID.randomUUID().toString(), "Oreo", "biscuit"));
log.info("sending product create command");
commandGateway.sendAndWait(new ProductCreateCommand(UUID.randomUUID().toString(), "Oreo", "biscuit"));
}
}
EDIT:
application.properties file
logging.level.root=info
server.port=8090
spring.datasource.url=jdbc:mysql://localhost:3306/producttest
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.use-new-id-generator-mappings= false
logs:
2021-01-06 21:44:51.229 INFO 21236 --- [ main] c.u.e.a.Client.TestRunner : sending product create command
2021-01-06 21:44:51.257 INFO 21236 --- [ main] c.u.e.a.Aggregate.ProductAggregate : handling ProductCreateCommand(productId=638643b7-1e4f-45b5-bfa9-1c2fd9360fa3, productName=Oreo, productDescription=biscuit)
2021-01-06 21:44:51.260 INFO 21236 --- [ main] c.u.e.a.Aggregate.ProductAggregate : applying ProductCreatedEvent(productId=638643b7-1e4f-45b5-bfa9-1c2fd9360fa3, productName=Oreo, productDescription=biscuit)
2021-01-06 21:44:51.321 WARN 21236 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1146, SQLState: 42S02
2021-01-06 21:44:51.321 ERROR 21236 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Table 'producttest.domain_event_entry' doesn't exist
2021-01-06 21:44:51.333 INFO 21236 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-01-06 21:44:51.346 ERROR 21236 --- [ main] o.s.boot.SpringApplication : Application run failed
In this case, you should instruct your application how to create tables.
You have 2 options:
Tell JPA to do that, adding spring.jpa.hibernate.ddl-auto=create or spring.jpa.hibernate.ddl-auto=update
Use a more robust tool like flyway, liquibase, etc
All the other configs you showed looks fine.

Spring AOP - Determine whether method was invoked by #Scheduled

I have a runtime annotation #MyAnnotation, and I would like to write an Aspect that determines whether the test() method below was called by:
Spring's #Scheduled framework
normal method invocation
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Scheduled(cron = "*/1 * * * * *") // scheduled to invoke every second
#MyAnnotation
public void test() {
// business logic
}
}
aspect code (pointcut + advice)
#Around(value="#annotation(myAnnotation)")
public Object featureToggle(ProceedingJoinPoint joinPoint, MyAnnotation myAnnotation) throws Throwable {
Boolean isInvoked = // TODO - is invoked by #Scheduled or not
}
Inspecting stack traces is always ugly, but of course you can do it:
package de.scrum_master.spring.q65397019;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {}
package de.scrum_master.spring.q65397019;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
#Scheduled(fixedRate = 1000)
// #Async
#MyAnnotation
public void doSomething() {}
}
package de.scrum_master.spring.q65397019;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
#SpringBootApplication
#Configuration
#EnableScheduling
#EnableAsync
public class DemoApplication {
public static void main(String[] args) throws InterruptedException {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) throws InterruptedException {
MyComponent myComponent = appContext.getBean(MyComponent.class);
myComponent.doSomething();
Thread.sleep(1000);
myComponent.doSomething();
}
}
package de.scrum_master.spring.q65397019;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Arrays;
#Aspect
#Component
public class MyAspect {
#Around("#annotation(myAnnotation)")
public Object advice2(ProceedingJoinPoint joinPoint, MyAnnotation myAnnotation) throws Throwable {
if (
Arrays.stream(new Exception().getStackTrace())
.map(StackTraceElement::toString)
.anyMatch(string -> string.contains("scheduling.support.ScheduledMethodRunnable.run("))
)
System.out.println(joinPoint + " -> scheduled");
else
System.out.println(joinPoint + " -> normal");
return joinPoint.proceed();
}
}
This will print something like:
(...)
2020-12-22 10:00:59.372 INFO 1620 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> scheduled
2020-12-22 10:00:59.456 INFO 1620 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-12-22 10:00:59.456 INFO 1620 --- [ main] d.s.spring.q65397019.DemoApplication : Started DemoApplication in 6.534 seconds (JVM running for 8.329)
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> normal
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> scheduled
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> normal
2020-12-22 10:01:00.475 INFO 1620 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler'
2020-12-22 10:01:00.477 INFO 1620 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
(...)
On Java 9+ you could use the stack walking API which would be more efficient than creating full stack traces from exception instances or querying them from the currently running thread.
Caveat: If you also annotate your scheduled method with #Async, then this will not work anymore because then the asynchronously running method does not have a stack trace in which you could identify that it was triggered by a ScheduledMethodRunnable or an application class.
Maybe you would like to achieve something like that:
#Slf4j
#Component
public class ScheduledTask {
#Scheduled(cron = "0/1 * * * * *")
#ScheduledTaskAnnotation(message = "ScheduledTaskMessage", number = 10)
public void doAction() {
log.debug("Task scheduled");
}
}
#Slf4j
#Aspect
#Component
public class ScheduledTaskAspect {
#Around("execution(public * *(..)) && #annotation(hu.gaszabo.sample.schedule.ScheduledTaskAnnotation)")
public void logScheduledTaskAction(final ProceedingJoinPoint p) {
log.debug("Aspect");
parameters(p).ifPresent(a -> {
log.debug("message: {}", a.message());
log.debug("number: {}", a.number());
});
try {
p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
private Optional<ScheduledTaskAnnotation> parameters(final ProceedingJoinPoint p) {
final Method method = ((MethodSignature) p.getSignature()).getMethod();
return Optional.ofNullable(AnnotationUtils.findAnnotation(method, ScheduledTaskAnnotation.class));
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(value = { ElementType.METHOD })
public #interface ScheduledTaskAnnotation {
String message() default "Message";
long number() default 0L;
}

Thymeleaf templatespring boot jpa can't get data fom the dabase,404 error

: Resource not found
2019-07-29 05:04:25.834 DEBUG 18656 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
2019-07-29 05:04:25.834 DEBUG 18656 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}
2019-07-29 05:04:25.834 DEBUG 18656 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-07-29 05:04:25.834 DEBUG 18656 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-07-29 05:04:25.834 DEBUG 18656 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Mon Jul 29 05:04:25 PDT 2019, status=404, error=Not Found, message=No message available, (truncated)...]
2019-07-29 05:04:25.835 DEBUG 18656 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404
2019-07-29 05:04:37.741 DEBUG 18656 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET "/favicon.ico", parameters={}
2019-07-29 05:04:37.742 DEBUG 18656 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/], class path resource []]
2019-07-29 05:04:37.754 DEBUG 18656 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
The problem is data already exist in the database but it's not being displayed in HTML table
EmployerController.java
package io.javabrains;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.hibernate.mapping.Index;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.javabrains.Entity.Employer;
#Controller
public class EmployerController {
#Autowired
private EmployerService service;
#RequestMapping("/")
public String newForm() {
return "form1";
}
public List<Employer>getAllEmployers()
{
return service.getAllEmployers();
}
#RequestMapping(value="/tables",method=RequestMethod.GET)
public String getAllEmployers(Model model)
{
List<Employer>employers = service.getAllEmployers();
model.addAttribute("Employer",employers);
return "tables";
}
#RequestMapping("/employer/{id}")
public Employer getEmployer(#PathVariable Integer id) {
return service.getEmployers(id);
}
#RequestMapping(method=RequestMethod.POST,value="/employer")
public void addEmployer(#RequestBody Employer employer) {
service.addEmployer(employer);
}
#RequestMapping(method=RequestMethod.PUT,value="/employer/{id}")
public void updateEmployer(#RequestBody Employer employer,#PathVariable int id) {
service.updateEmployer(id,employer);
}
#RequestMapping(method=RequestMethod.DELETE,value="/create/{id}")
public void deleteEmployer(#PathVariable int id)
{
service.deleteEmployer(id);
}
EmployerService.java
package io.javabrains;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import io.javabrains.Entity.Employer;
#Service
public class EmployerService {
#Autowired
private Repository repository;
public List<Employer>getAllEmployers(){
List<Employer>employers = new ArrayList<>();
repository.findAll()
.forEach(employers::add);
return employers;
}
public void addEmployer(Employer employer) {
repository.save(employer);
}
public void updateEmployer(int id, Employer employer) {
repository.save(employer);
}
public void deleteEmployer(int id) {
repository.deleteById(id);
;
}
public Employer getEmployers(int id)
{
return repository.getOne(id);
}
}
Employer.Java
package io.javabrains;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import io.javabrains.Entity.Employer;
#Service
public class EmployerService {
#Autowired
private Repository repository;
public List<Employer>getAllEmployers(){
List<Employer>employers = new ArrayList<>();
repository.findAll()
.forEach(employers::add);
return employers;
}
public void addEmployer(Employer employer) {
repository.save(employer);
}
public void updateEmployer(int id, Employer employer) {
repository.save(employer);
}
public void deleteEmployer(int id) {
repository.deleteById(id);
;
}
public Employer getEmployers(int id)
{
return repository.getOne(id);
}
}
table.html
<tbody>
<tr th:each="$(employers)">
<td th:text="${employers.name}"></td>
<td th:text="${employer.position}"></td>
<td th:text="${employer.office}"></td>
<td th:text="${employer.age}"></td>
<td th:text="${employer.salary}"></td>
</tr>
......
Assuming the method you are testing is this:
#RequestMapping(value="/tables",method=RequestMethod.GET)
public String getAllEmployers(Model model)
{
List<Employer>employers = service.getAllEmployers();
model.addAttribute("Employer",employers);
return "tables";
}
for the tables.html file you should have something like this
<tr th:each="${Employer}">
matching the name attribute with your html file.
Plus, at your service you can just do
repository.findAll()
This guide might help you https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation

Overriding Bean Configuration with annotations

Use case: I have my container configured via classpath scanning #ComponentScan. For my test configuration I need the ability to mock specific beans.
Due to the order of loading, beans loaded via classpath scan are not overriding properly when using #Configuration. The following code samples demonstrate the problem. BaseExample.java shows how it is possible to override beans via configuration. ScanExample.java shows that overriding a bean that was loaded via #ComponentScan is skipped (see final note).
A demo project is available on bitbucket.
// BaseExample.java
package com.glassworks.mock;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Joiner;
public class BaseExample {
private static final Logger log = LoggerFactory.getLogger(BaseExample.class);
private static AnnotationConfigApplicationContext ctx;
public static void main(String args[]) {
ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class);
String beans[] = ctx.getBeanDefinitionNames();
log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans));
for(String bean : beans) {
log.info("{}: {}", bean, ctx.getBean(bean));
}
}
#Configuration
public static class Config {
#Bean
public AccountDao accountDao() {
log.debug("Creating accountDao [Config]");
return new AccountDao();
}
}
#Configuration
public static class OverrideConfig {
#Bean
public Object accountDao() {
log.debug("Creating accountDao [OverrideConfig]");
return Mockito.mock(AccountDao.class);
}
}
}
Output:
21:05 INFO | com.glassworks.mock.BaseExample | accountDao: Mock for AccountDao, hashCode: 666537607
[// ScanExample.java
package com.glassworks.mock;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Joiner;
public class ScanExample {
private static final Logger log = LoggerFactory.getLogger(ScanExample.class);
private static AnnotationConfigApplicationContext ctx;
public static void main(String args[]) {
ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class);
String beans[] = ctx.getBeanDefinitionNames();
log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans));
for(String bean : beans) {
log.info("{}: {}", bean, ctx.getBean(bean));
}
}
#Configuration
#ComponentScan("com.glassworks.services")
public static class Config {
}
#Configuration
public static class OverrideConfig {
#Bean
public AccountDao accountDao() {
log.debug("Creating accountDao [OverrideConfig]");
return Mockito.mock(AccountDao.class);
}
}
}
Output:
21:08 INFO | com.glassworks.mock.ScanExample | accountDao: com.glassworks.services.AccountDao#48805ebb
// AccountDao.java
package com.glassworks.services;
import org.springframework.stereotype.Repository;
#Repository
public class AccountDao {
}
Note
Its worth noting on that with logging set to debug, Spring indicates that it is skipping over the definition. This appears to be a bug.
21:09 DEBUG | o.s.c.a.ConfigurationClassBeanDefinitionReader | Skipping loading bean definition for [BeanMethod:name=accountDao,declaringClass=com.glassworks.mock.ScanExample$OverrideConfig]: a definition for bean 'accountDao' already exists. This is likely due to an override in XML.

Resources