OpenEntityManagerInViewInterceptor analog for CLI application? - java

Is there any analog of OpenEntityManagerInViewInterceptor for CLI application?
I am trying to use Spring's CrudRepository with JPA data source backed by Hibernate 4 in CLI application.
My main method creates an instance of the class containing this method and injects services using context.getBeanFactory().autowireBean(object);.
Services for data fetching have methods annotated with #Transactional. These methods invoke CrudRepository's methods.
But I receive org.hibernate.LazyInitializationException when I try to manage related entities in the CLI application.
Is there any workaround to have lazy loading working in the CLI application outside the #Transactional methods like OpenEntityManagerInViewInterceptor for web applications?
Look at the following snippet:
public class test {
#Autowired
public UserService userService;
public static void main(String[] args) {
test test = new test();
//injecting dependencies into test
test.run();
}
private void run() {
User user = userService.findById(42);
System.out.println(user.getLogin()); //User was fetched successfully
Address address = new Address("London");
user.addAddress(address);//Exception in thread "main" java.lang.RuntimeException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection
}
}

I would say there isn't one for CLI because there's no notion of View as in MVC pattern. However I suspect your problem is due to ineffective declarative transaction management.
For a CLI application, ensure you have properly setup datasource, EntityManagerFactory and declarative transaction managemeent.
A good practice is to enclose your business / DAO code in service / repository classes annotated with #Service, #Repository, #Component or other appropriate spring annotation, create your own ApplicationContext and obtain reference to your services:
// UserDAO.java ----------------------------------------
#Repository
public class UserDAO {
#PersistenceContext private EntityManager em;
#Transactional
public User findById(long id) {
// ...
}
}
// UserService.java ----------------------------------------
#Service
public class UserService {
#Autowired private UserDAO userDAO;
// ...
}
// MainClass.java ----------------------------------------
public class MainClass {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/root-context.xml");
UserService userService = context.getBean(UserService.class);
// more code here..
context.close();
}
}
Autowiring and declarative transaction will still work as per normal as long as the bean is created by Spring container.

Related

How to add sample data to database with springboot?

I've a Spring Boot application with the Spring Data JPA and two entities mapped to a database. My application is set to recreate the database on every startup. Now i want to create some instances of these POJOs, persist them in the database so I've some data to test my application with in the ongoing development process.
What is the best way to do that?
What i tried so far:
My class to add the sample data
public class DBSampleAdditor {
#PersistenceContext
private EntityManager em;
#Transactional
public void addSampleData() {
// creating POJO instances and persist them with em.perists()
}
}
I tried to call these functions in the main of my ApplicationClass
#SpringBootApplication()
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
That didnt work at all, as the EntityManager never got an Instance from the persistence unit.
I also tried to create a CommandLineRunner:
#Component
public class PTCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Adding sample data...");
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
But that one seemed to never been called in the startup process.
You can use method with #PostConstruct with #Component to insert data at startup.
#PostConstruct will be invoked after the bean has been created, dependencies have been injected, all managed properties are set, and before the bean is actually set into scope.
Just inject your database repository in the class marked with #Component and call your injected database repository inside the method marked with #PostConstruct
Example:
#Component
public class DbInit {
#Autowired
private UserRepository userRepository;
#PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}
In order to use spring managed components like the EntityManager the objects need to spring Beans, which basically means they are created inside the spring context and can utilise the IoC container.
In the example above, running the DbSampleAdditor class from the main method is outside the spring context, so the beans like PersistenceContext wont get injected.
One fairly simple way to do this inside the spring context is to add an ApplicationListener for the ApplicationReadyEvent
#Component
class Initialise implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
private final DbSampleAdditor db;
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
db.addSampleData();
}
}
When the application ready event fires all the necessary spring bean wiring is set up so when it runs the addSampleData method, things like the EM are good to go.
Other approaches for setting up a DB for spring boot are documented here
Instead of creating an object DBSampleAdditor, try to autowire it so that it is available on application startup. #Autowired DBSampleAdditor DBSampleAdditor Then you try to add sample data within the run method

Using Mockito on one test method making other Test methods fail

I am working on creating integration test for a service class i am testing and I needed to mock the dao for one of the test methods. the problem is when i run the tests together some of my tests fail but when i run them individually the tests past. If i remove the mockito part all my tests pass when i run them all at once. any insight on this is appreciated
below is my code:
// here is my Service class
public class Service {
Dao dao;
public Dao getDao() {
return dao;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
//here is my integ test
#Category(IntegrationTest.class)
#RunWith(SpringRunner.class)
public class Test{
#Rule
public ExpectedException thrown = ExpectedException.none();
#Autowired
#Qualifier(Service.SERVICE_NAME)
protected Service service;
#Before
public void setUp() {
assertNotNull(service);
}
#Test
public void testDoSomethingOne() throws Exception {
Dao dao = Mockito(Dao.class)
service.setDao(dao)
boolean flag = service.doSomething();
Assert.assertTrue(flag);
}
#Test
public void testDoSomethingTwo() throws Exception {
Integer num = service.doSomething();
Assert.assertNotNull(num);
}
The test method testDoSomethingOne() sets the mock dao for the service instance which it retains for rest of the tests.
Annotate the method testDoSomethingOne() with #DirtiesContext to get a fresh context associated with the subsequent test method.
Test annotation which indicates that the ApplicationContext associated
with a test is dirty and should therefore be closed and removed from
the context cache.
Use this annotation if a test has modified the
context — for example, by modifying the state of a singleton bean,
modifying the state of an embedded database, etc. Subsequent tests
that request the same context will be supplied a new context.
You can get the dao before each test and assign it back to service after the test
something like this:
private static Dao dao;
#Before
public void setUp() {
if(dao == null) {
dao = service.getDao();
}
}
#After
public void tearDown() {
service.setDao(dao);
}
If it is a integration test you should not mock your daos, the recommended way is to use a in memory database like H2. The spring folks already provide the annotation #DataJpaTest that creates the database for you.
You can use the #DataJpaTest annotation to test JPA applications. By default, it scans for #Entity classes and configures Spring Data JPA repositories. If an embedded database is available on the classpath, it configures one as well. Regular #Component beans are not loaded into the ApplicationContext.
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test

Integration test for spring jdbc

Let's say we have a service layer :
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyDAO myDAO;
#Transactional
public void myMethod(SomeObject someObject) {
myDAO.insertIntoMyDb(someObject);
}
}
Let us say myDAO uses spring jdbc :
#Repository
public class MyDAOImpl implements MyDAO {
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
#Override
public void insertIntoMyDb(SomeObject object) {
// some code before this where we get query and param
int numberOfRowsUpdated = jdbcTemplate.update(query,param);
if(numberOfRowsUpdated != 1)
throw new Exception("Error while inserting a record in database. Actual records inserted : " + numberOfRowsUpdated);
}
}
I want to write 2 tests.
The first test will check my dao layer only. I want to make a jdbc call here , get data and verify. I don't want to mock them.
The second test is integration test. I want my service layer to call Dao layer. Note that there is transaction. Now this will give me data from DAO. Again DAO has to connect to db to get data.
I am using spring boot. My database properties are present in applicationITest.properties file.
How to do these 2 testing ? What is the correct annotations I have to use ? Can some one provide an example ?
a) Rollback will happen by default as long as you annotate your Test class with #Transactional. Documentation about Test-managed transactions.
sample test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = MyTestConfig.class)
#Transactional
public class MyClassTest {
//..
#Test
public void myTest() {
//..
}
//..
}
b) Yes you can enforce the commit using the #Commit (added in Spring 4.2) test annotation. Here's the documentation.

Hibernate EntityListener with Guice

I am trying to Inject a class into my entity listener created by my jpa provider. The following code throws a PersistenceException - unable to build hibernate SessionFactory (which I expected). From my research it seems like jpa 2.1 supports dependency injection. I am missing some type of binding/provides for guice to know about this class. My guess is it has to be some type of run time binding.
public class EntityListener {
private SomeService service;
#Inject
public EntityListener(SomeService service) {
this.service = service;
}
#PostLoad
public void load(Object o){
//use service here
}
}

Spring: create mocks instead of real objects

I'm using Spring annotation based configuration in my Play application.
Controllers and DAOs are Spring beans. Controller and DAO layers are defined with different Spring profiles and each layer could be disabled separately.
I'd like to test controller layer in isolation from DAO layer. I've disabled DAO profile and redefined each of DAO beans as a Mockito mock. From functional point of view it works fine, the only thing I don't like is defining mocks manually like this:
#Configuration
#Import(AppContext.class)
public class TestAppContext {
#Bean
public DaoA getDaoA(){
return mock(DaoA.class);
}
//... all dependencies are re-defined manually
}
Is there a way to define package (like with #ComponentScan annotation)
and get all beans in that package as mocks instead of real objects?
UPD:
I'm running tests with FakeApplication (https://www.playframework.com/documentation/2.0/api/java/play/test/FakeApplication.html), so context is started not in the test level, but inside fake application startup.
public class ControllerTest extends WithApplication {
#Before
public void setUp() throws Exception {
start(fakeApplication(new GlobalSettings(){
private ApplicationContext appContext;
public void onStart(Application app) {
appContext = new AnnotationConfigApplicationContext(TestAppContext.class);
}
#Override
public <A> A getControllerInstance(Class<A> clazz) throws Exception {
return appContext.getBean(clazz);
}
}));
}
...
}
I did it like this because I wan't to make the test more reliable and test how controller works in real environment:
#Test
public void testControllerMethod() {
Result result = route(fakeRequest(GET, "/controller/method"));
assertThat(result).is(...);
}
If the number of dependencies you need to mock is huge, you can also use spring-auto-mock.
#ContextConfiguration(classes = { AutoMockRegistryPostProcessor.class, RestOfClasses.class, ... })
#RunWith(SpringJUnit4ClassRunner.class)
public class YourTest {
...
}
As you are creating the ApplicationContext on your own, you can register the postprocessor programmatically:
public void onStart(Application app) {
appContext = new AnnotationConfigApplicationContext(TestAppContext.class);
appContext.getBeanFactory().addBeanPostProcessor(new AutoMockRegistryPostProcessor())
}
Mark your unit-test with #RunWith(SpringJUnit4ClassRunner.class)
Mark your tested class as #InjectMock
Mark you Dao class as #Mock
Make use of Mockito in your project

Categories

Resources