jpa dirty checking not working in #SpringBootApplication - java

jpa dirty checking not working in #SpringBootApplication
I use initDb in #SpringBootApplication like this
#SpringBootApplication
#EnableJpaAuditing
#EnableScheduling
#RequiredArgsConstructor
public class main {
private final PoiRepository poiRepository;
private final PoiCategoryRepository poiCategoryRepository;
public static void main(String[] args) {
SpringApplication.run(main.class, args);
}
//Dummy data poiCategory
#Bean
#Profile("local")
#Transactional
public CommandLineRunner createPoiCategory(PoiCategoryRepository repository) {
return args -> {
if (repository.findAll().size() == 0) {
PoiCategory parent = repository.save(PoiCategory.builder()
.name("parent")
.imageFileName("test")
.build());
PoiCategory child = repository.save(PoiCategory.builder()
.name("child")
.imageFileName("test2")
.build());
parent.addChildCategory(child);
System.out.println("parent = " + parent);
parent.setImageFileName("1234");
System.out.println("parent.getImageFileName() = " + parent.getImageFileName());
}
};
}
}
parent.addChildCategory(parent) and
parent.setImageFileName("1234") is not changed
but if I use
repository.save(parent);
repository.save(child);
then parent and child will update
why jpa's dirty checking and update is not work?

It is because #Tranasctional does not work if you declare it on the #Bean method. It should be annotated on the actual method you want to be executed. So your codes now actually will not have any transaction wrapped around CommandLineRunner#run() and dirty checking only works if the entities are modified within a transaction.
Changing to the following should solve your problem :
public class MyCommandLineRunner implements CommandLineRunner {
private PoiCategoryRepository poiCategoryRepository;
public MyCommandLineRunner(PoiCategoryRepository poiCategoryRepository) {
this.poiCategoryRepository = poiCategoryRepository;
}
#Transactional
#Override
public void run(String... args) throws Exception {
// put your codes that you want to execute within a transaction here....
}
}
And define the MyCommandLineRunner bean :
#Bean
public MyCommandLineRunner myCommandLineRunner(PoiCategoryRepository poiCategoryRepository) {
return new MyCommandLineRunner(poiCategoryRepository);
}
It will update the parent and the child now if you call save() on the JpaRepository simply because the implementation already had #Tranasctional annotated on this method.

Related

Call service when new data is inserted

I have this scheduler which is used to check for new data into DB table:
#Service
public class ReloadCache {
#Scheduled(fixedDelay = 1000)
public void reload() {
...... do something
}
}
......
#Service
public class LogicalClient {
final Map<String, Map<String, String>> map;
#PostConstruct
public void initializeBalances() {
............ map = new HashMap.......
}
#KafkaListener(......")
public void handle(....) {
.......read map here
}
}
Note that these two services are located in separate Java classes and packages.
When schedule runs and change is detected how how I can call again Java method initializeBalances annotated with #PostConstruct in order to generate again the map structure?
Inject your LogicalClient in your ReloadCache class and call that function like this:
#Service
public class ReloadCache {
private final LogicalClient logicalClient;
public ReloadCache(LogicalClient client) // Injection through constructor.
{
this.logicalClient = client;
}
#Scheduled(fixedDelay = 1000)
public void reload() {
...... do something
client.initializeBalances()
}
}
Both your classes are annotated with the #Service. So they are both Spring beans that can be injected wherever you find it suitable (as long as the receiving class is a bean itself).

Spring boot #Value is null

Before we start, yes I know there's another question out there but they are not the same issue and I couldn't find anything to solve this.
Ok, I have
package a
imports ...
#SpringBootApplication
public class LauncherApplication implements CommandLineRunner {
#Autowired
private SubListener subListener;
#Value("${proj.id}")
private String testy;
public static void main(String[] args) {
SpringApplication.run(LauncherApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println(subListener.getProjID());
System.out.println(testy);
}
}
Then on my subListener
package a.b
imports ...
#Component
public class SubListener {
#Value("${proj.id}")
private String projID;
public String getProjID() {
return projID;
}
}
and finally inside my resources folder on application.properties
proj.id=Hello from file
Now, by all accounts this should work, the SpringBootApplication has the component scan thing, the bean is marked as #component and is a subpackage of the springbootapplication, the properties file has the default name and in the default directory. I can't find a single reason for this not to work. Also mind you that when I call the property on testy it works, it only return null when it's returning from the sublistener.
Thank you for your time
EDIT:
New launcherApplication
#SpringBootApplication
public class LauncherApplication {
public static void main(String[] args) {
SpringApplication.run(LauncherApplication.class, args);
}
#Bean
public CommandLineRunner runner(SubListener subListener){
CommandLineRunner runner = new CommandLineRunner() {
#Override
public void run(String... args) throws Exception {
System.out.println(subListener.getProjID());
}
};
return runner;
}
}
It still returns null though
My only guess would be that the #Value annotation inside your SubListener class is from the wrong package. Can you please check that you are using this import and not something else:
import org.springframework.beans.factory.annotation.Value;
I've copied your code and it's working for me. If you still can't get it working then I'd recommend trying to reproduce it in a new empty project.

How to combine Mockito and Spring in TestNG

We are building an application which uses Spring Boot. We write unit tests using TestNG and Mockito. However I find it pretty annoying to write when(...) configuration, I would like to use real components instead. I started to use #Spy components instead of mocks and this works pretty well until I need to put a Spy into a Spy. I'd like to avoid loading a Spring Context if possible, because creation of the context is very slow it looks like overkill for me to load it for at max 5 classes.
Is there any way, how could I use real code instead of Mocks and not loading whole Spring context? Or is my approach wrong at all and I should mock out all other classes then the tested one?
The other way to do this and may take some modifying of code on your end is to do it by constructor injection instead of field injection. Basically taking away any need of the spring context for testing. so the same from the other answer
Class to test
#Service
public class RecordServiceImpl implements RecordService
{
private final RecordRepository recordRepository;
#Autowired
public RecordServiceImpl(RecordRepository recordRepository)
{
this.recordRepository = recordRepository;
}
public Record find(String id)
{
return recordRepository.findOne(id);
}
public List<Record> findAll()
{
return recordRepository.findAll();
}
#Transactional
public Record save(Record record)
{
record.setRecordStatus("F");
return recordRepository.save(record);
}
}
Test Case
//#RunWith(SpringJUnit4ClassRunner.class)
//#ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
// #Autowired
private RecordRepository recordRepository = Mockito.mock(RecordRepository.class);
// #Autowired
private RecordService recordService;
#Before
public void setup()
{
Mockito.reset(recordRepository);
recordService = new RecordServiceImpl(recordRepository);
}
#Test
public void testFind()
{
Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);
Record record = recordService.find("1");
Assert.assertNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
}
#Test
public void testSave()
{
Mockito.when(recordRepository.save(Mockito.any(Record.class)))
.thenAnswer(new Answer<Record>()
{
#Override
public Record answer(InvocationOnMock invocation) throws Throwable
{
Record record = (Record) invocation.getArguments()[0];
Assert.assertEquals("F", record.getRecordStatus());
return record;
}
});
Record record = new Record();
record = recordService.save(record);
Assert.assertNotNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
}
#Test
public void findAll()
{
Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());
List<Record> records = recordService.findAll();
Assert.assertNotNull(records);
Assert.assertEquals(0, records.size());
Mockito.verify(recordRepository, Mockito.times(1)).findAll();
}
}
I think your looking for like this with the use of #ContextConfiguration and #Configuration
Class to test
#Service
public class RecordServiceImpl implements RecordService
{
#Autowired
private RecordRepository recordRepository;
public Record find(String id)
{
return recordRepository.findOne(id);
}
public List<Record> findAll()
{
return recordRepository.findAll();
}
#Transactional
public Record save(Record record)
{
record.setRecordStatus("F");
return recordRepository.save(record);
}
}
Test Class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
#Autowired
private RecordRepository recordRepository;
#Autowired
private RecordService recordService;
#Before
public void setup()
{
Mockito.reset(recordRepository);
}
#Test
public void testFind()
{
Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);
Record record = recordService.find("1");
Assert.assertNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
}
#Test
public void testSave()
{
Mockito.when(recordRepository.save(Mockito.any(Record.class)))
.thenAnswer(new Answer<Record>()
{
#Override
public Record answer(InvocationOnMock invocation) throws Throwable
{
Record record = (Record) invocation.getArguments()[0];
Assert.assertEquals("F", record.getRecordStatus());
return record;
}
});
Record record = new Record();
record = recordService.save(record);
Assert.assertNotNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
}
#Test
public void findAll()
{
Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());
List<Record> records = recordService.findAll();
Assert.assertNotNull(records);
Assert.assertEquals(0, records.size());
Mockito.verify(recordRepository, Mockito.times(1)).findAll();
}
}
Test Class Configuration
#Configuration
public class RecordServiceTestConfig
{
#Bean
public RecordService recordService()
{
return new RecordServiceImpl();
}
#Bean
public RecordRepository recordRepository()
{
return Mockito.mock(RecordRepository.class);
}
}
the entire test class took 714ms to run the findAll test took 1ms.
If you are looking to configure your testcase using testng with Spring then you to mention
#ContextConfiguration(locations={
"/context.xml","/test-context.xml"})
at class level to load you spring file and extends your class org.springframework.test.context.testng.AbstractTestNGSpringContextTests
Sample
https://dzone.com/articles/spring-testing-support-testng

How use #Autowired with Annotation config?

i create simple spring project and i need to use annotation #Autowired but when i run project, i get exception NullPointerException.
This is my classes:
Main.java
public class Main {
#Autowired
private static InjectClass injectClass;
public static void setInjectClass(InjectClass injectClass) {
Main.injectClass = injectClass;
}
public static void main(String[] args) {
injectClass.hello(); //NullPointerException
}
}
ConfigurationBean
#Configuration
public class ConfigurationBean {
#Bean
public InjectClass injectClass(){
return new InjectClass();
}
}
InjectClass
public class InjectClass {
public void hello(){
System.out.println("Autowired success!");
}
}
You need to initiate application contex before using any bean.
You can do it by writing following code in starting of your main method.
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
ConfigurationBean.class);

Jersey Test #Autowired field in tested class is null

I have a little problem. I think this is typical question. However, I can't find good example. My application is using Jersey. And I want to test controller by client as test. Controller has private field - StudentService. When I debug test I see, that field is null. This leads to error. And I need to inject this field. I tried this:
My Controller
#Path("/student")
#Component
public class StudentResourse {
#Autowired
private StrudentService service; // this field Spring does not set
#Path("/getStudent/{id}")
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Student getStudent(#PathParam("id") long id) {
return service.get(id);
}
}
My JUnit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:config.xml")
#TestExecutionListeners({ DbUnitTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class StudentResourseTest extends JerseyTest {
private static final String PACKAGE_NAME = "com.example.servlet";
private static final String FILE_DATASET = "/data.xml";
#Autowired
private StudentService service; // this field is setted by Spring, but I do not need this field for test
public StudentResourseTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();
}
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("restful.server.resource")
.contextParam("contextConfigLocation",
"classpath:/config.xml").contextPath("/")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build();
}
#Test
#DatabaseSetup(FILE_DATASET)
public void test() throws UnsupportedEncodingException {
ClientResponse response = resource().path("student").path("getStudent")
.path("100500").accept(MediaType.APPLICATION_XML)
.get(ClientResponse.class);
Student student = (Student) response.getEntity(Student.class);
} }
I guees, that problem is in test class. Because, when I run my application not in test, I can directly request students and everything working fine. But when I test classes, internal field of Controller does not setted. How to fix this bug? Thanks for your answers.
This is in my config.xml
<context:component-scan base-package="com.example" />
<bean id="StudentResourse" class="com.example.servlet.StudentResourse">
<property name="service" ref="studentService" />
</bean>
<bean id="service" class="com.example.service.StudentServiceImpl" />
One issue may be that you're trying to configure your test application in constructor and in configure() method. Use one or another but not both because in this case your configure() method is not invoked and hence you may not be using SpringServlet and everything that is defined in this method.
Reference: https://github.com/jiunjiunma/spring-jersey-test and http://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/
Idea is to get a hold of the application context inside jersey by using ApplicationContextAware interface. There after we can grab the exact bean already created by spring, in your case, StudentService. Below example shows a mocked version of the dependency, SampleService, used to test the resource layer apis.
Resource class delegating the processing to a service layer
#Component
#Path("/sample")
public class SampleResource {
#Autowired
private SampleService sampleService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path ("/{id}")
public Sample getSample(#PathParam("id") int id) {
Sample sample = sampleService.getSample(id);
if (sample == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return sample;
}
}
Service layer encapsulating business logic
#Service
public class SampleService {
private static final Map<Integer, Sample> samples = new HashMap<>();
static {
samples.put(1, new Sample(1, "sample1"));
samples.put(2, new Sample(2, "sample2"));
}
public Sample getSample(int id) {
return samples.get(id);
}
}
Unit test for the above resource
public class SampleResourceTest extends SpringContextAwareJerseyTest {
private SampleService mockSampleService;
// create mock object for our test
#Bean
static public SampleService sampleService() {
return Mockito.mock(SampleService.class);
}
/**
* Create our own resource here so only the test resource is loaded. If
* we use #ComponentScan, the whole package will be scanned and more
* resources may be loaded (which is usually NOT what we want in a test).
*/
#Bean
static public SampleResource sampleResource() {
return new SampleResource();
}
// get the mock objects from the internal servlet context, because
// the app context may get recreated for each test so we have to set
// it before each run
#Before
public void setupMocks() {
mockSampleService = getContext().getBean(SampleService.class);
}
#Test
public void testMock() {
Assert.assertNotNull(mockSampleService);
}
#Test
public void testGetSample() {
// see how the mock object hijack the sample service, now id 3 is valid
Sample sample3 = new Sample(3, "sample3");
Mockito.when(mockSampleService.getSample(3)).thenReturn(sample3);
expect().statusCode(200).get(SERVLET_PATH + "/sample/3");
String jsonStr = get(SERVLET_PATH + "/sample/3").asString();
Assert.assertNotNull(jsonStr);
}
}
SpringContextAwareJerseyTest
#Configuration
public class SpringContextAwareJerseyTest extends JerseyTest {
protected static String SERVLET_PATH = "/api";
final private static ThreadLocal<ApplicationContext> context =
new ThreadLocal<>();
protected String getResourceLocation() {
return "example.rest";
}
protected String getContextConfigLocation() {
return getClass().getName();
}
static private String getContextHolderConfigLocation() {
return SpringContextAwareJerseyTest.class.getName();
}
protected WebAppDescriptor configure() {
String contextConfigLocation = getContextConfigLocation() + " " +
getContextHolderConfigLocation();
Map<String, String> initParams = new HashMap<>();
initParams.put("com.sun.jersey.config.property.packages",
getResourceLocation());
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
return new WebAppDescriptor.Builder(initParams)
.servletClass(SpringServlet.class)
.contextParam(
"contextClass",
"org.springframework.web.context.support.AnnotationConfigWebApplicationContext")
.contextParam("contextConfigLocation", contextConfigLocation)
.servletPath(SERVLET_PATH) // if not specified, it set to root resource
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
protected final ApplicationContext getContext() {
return context.get();
}
#Bean
public static ContextHolder contextHolder() {
return new ContextHolder();
}
private static class ContextHolder implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context.set(applicationContext);
}
}
}
Using the above with jersey 1.8

Categories