AuditingEntityListener junit cannot find context - java

I have an entity and would like to implement Audit and AuditHistory, both works but while unit testing Application context is null.
The Entity
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#EntityListeners(UserListener.class)
public class User extends BaseModel<String> {
#Column
private String username;
#Column
private String password;
#Transient
private String passwordConfirm;
#ManyToMany
private Set<Role> roles;
}
UserListener
public class UserListener {
#PrePersist
public void prePersist(User target) {
perform(target, INSERTED);
}
#PreUpdate
public void preUpdate(User target) {
perform(target, UPDATED);
}
#PreRemove
public void preRemove(User target) {
perform(target, DELETED);
}
#Transactional(MANDATORY)
void perform(User target, Action action) {
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
if(target.isActive()){
entityManager.persist(new UserAuditHistory(target, action));
}else{
entityManager.persist(new UserAuditHistory(target, DELETED));
}
}
}
UserAuditHistory
#Entity
#EntityListeners(AuditingEntityListener.class)
public class UserAuditHistory {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "FK_user_history"))
private User user;
#CreatedBy
private String modifiedBy;
#CreatedDate
#Temporal(TIMESTAMP)
private Date modifiedDate;
#Enumerated(STRING)
private Action action;
public UserAuditHistory() {
}
public UserAuditHistory(User user, Action action) {
this.user = user;
this.action = action;
}
}
BeanUtil for getting and setting context
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Now the Junit where I get null pointer exception on the context from the above BeanUtil class in getBean() method.
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTest{
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void whenFindAll_theReturnListSize(){
entityManager.persist(new User("jk", "password", "password2", null));
assertEquals(repository.findAll().size(), 1);
}
}

This is how I solved the problem, in the test class
#Autowired
ApplicationContext context;
inside the test method called
BeanUtil beanUtil = new BeanUtil();
beanUtil.setApplicationContext(context);
and it worked.

The problem is, that you're not using spring's AOP, but the static context:
private static ApplicationContext context;
It's null, because not creating #Bean leads to unproxied objects. The solution would be to #Autowire it.

Related

Spring Boot - "this.peopleService" is null?

I'm a bit new to Spring Boot and I'm trying to create model/repo/service/serviceImp/controller type of architecture.
After I try to make a this get request:
http://localhost:8080/api/v1/people/name?name=steve
and I get this error (I created a couple of people in DB):
"java.lang.NullPointerException: Cannot invoke \"com.project.Springbootbackend.service.PeopleService.findAllByName(String)\" because \"this.peopleService\" is null\r\n\tat com.project.Springbootbackend.controller.PeopleController.findAllByName(PeopleController.java:24)
This is my code:
People(entity)
#Entity
public class People {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Column(name = "email")
private String email;
...
//constructor + get/set
PeopleController
#RestController
#RequestMapping("/api/v1/people")
#RequiredArgsConstructor
public class PeopleController {
private PeopleService peopleService;
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
PeopleRepo
public interface PeopleRepository extends JpaRepository<People, Integer> {
List<People> findAllByName(String name);
}
PeopleService
public interface PeopleService {
List<People> findAllByName(String name);
}
PeopleServiceImp
#RequiredArgsConstructor
#Service
public class PeopleServiceImp implements PeopleService {
PeopleRepository peopleRepository;
#Override
public List findAllByName(String name) {
return (List) ResponseEntity.ok(peopleRepository.findAllByName(name));
}
}
Thx guys in advance.
*SOLUTION:
Entity, service & repository is the same.
ServiceImp and controller changes are down belowe:
Controller:
#RestController
#RequestMapping("/api/v1/people")
public class PeopleController {
private PeopleService peopleService;
public PeopleController(PeopleService peopleService) {
this.peopleService = peopleService;
}
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
ServiceImp
#Service
public class PeopleServiceImp implements PeopleService {
private PeopleRepository peopleRepository;
public PeopleServiceImp(PeopleRepository peopleRepository) {
this.peopleRepository = peopleRepository;
}
#Override
public List<People> findAllByName(String name) {
List<People> people = peopleRepository.findAllByName(name);
return people;
}
}
Your constructor does not inject the service, because of the RequiredArgsConstructor (see Link) needs special treatment. Therefore, use final:
#RestController
#RequestMapping("/api/v1/people")
#RequiredArgsConstructor
public class PeopleController {
private final PeopleService peopleService;
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
Same here:
#RequiredArgsConstructor
#Service
public class PeopleServiceImp implements PeopleService {
private final PeopleRepository peopleRepository;
#Override
public List findAllByName(String name) {
return (List) ResponseEntity.ok(peopleRepository.findAllByName(name));
}
}
Additional hint, use a typed list:
#Override
public List<People> findAllByName(String name) {
return ResponseEntity.ok(peopleRepository.findAllByName(name));
}
Try like this:
#Autowired
private PeopleService peopleService;
#Autowired
private PeopleRepository peopleRepository;
You also need to add the #SpringBootApplication annotation in the main class of the application.
Something like that:
#SpringBootApplication
class PeopleApplication {
public static void main(String[] args) {
...
Take a look at this article about automatic dependency injection in Spring:
https://www.baeldung.com/spring-autowire
You missed the autowiring annotation in the controller to inject the service which may make this.peopleService to be null.
#Autowired
private PeopleService peopleService;
You also need to do autowire in your serviceimpl class
#Autowired
private PeopleRepository peopleRepository;

Hibernate is not using the proxy of a session scope bean spring

I'm using Spring #Scope(value = "session", proxyMode=ScopedProxyMode.TARGET_CLASS) beans for objects that should be shared across a single Http-Session. This will provide for example one "Project" object for each User who is using my application.
To get this working I had to implement an interceptor for Hibernate that is returning the name of the class:
public class EntityProxySupportHibernateInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 7470168733867103334L;
#Override
public String getEntityName(Object object) {
return AopUtils.getTargetClass(object).getName();
}
}
With this interceptor I can use a Spring CrudRepository to save a Project-entity in the database:
#Repository
public interface ProjectRepository extends CrudRepository<Project, Integer> {
Project findByProjectId(int projectId);
}
Project-entity:
#Component
#Entity
#Table(name = "xxx.projects")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Project implements Serializable {
private static final long serialVersionUID = -8071542032564334337L;
private int projectId;
private int projectType;
#Id
#Column(name = "project_id")
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
#Column(name = "project_type")
public int getProjectType() {
return projectType;
}
public void setProjectType(int projectType) {
this.projectType = projectType;
}
}
Storing the Project in the database works as expected. I can have a look at the database and the correct values are inserted. Now I have a different entity that I'm creating the same way as the project and that I want to save in the database via a CrudRepository.
Here the problem begins. Hibernate is not inserting the values that I have set. Hibernate always only inserts null into the database. Reading the values in my Spring application is working as expected. I think that Hibernate is not using the proxy of the entity but the underlying blueprint of the object. How can I force Hibernate to use the proxy with the correct values?
Repository:
#Repository("DataInput001Repository")
public interface DataInputRepository extends CrudRepository<DataInput, DataInputId> {}
Entity:
#Component("DataInput001")
#Entity
#Table(name = "xx.data_input_001")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#IdClass(DatanputId.class)
public class DataInput implements Serializable {
private static final long serialVersionUID = -6941087210396795612L;
#Id
#Column(name = "project_id")
private int projectId;
#Column(name = "income")
private String income;
#Column(name = "income_increase")
private String incomeIncrease;
/* Getter + Setter */
}
Service:
#Service("DataInputService001")
public class DataInputServiceImpl implements DataInputService {
#Resource(name = "DataInputMapper001")
DataInputMapperImpl dataInputMapper;
#Resource(name = "DataInput001Repository")
DataInputRepository dataInputRepository;
#Resource(name = "DataInput001")
DataInput datanInput;
#Transactional
public void createDataInput(String json) throws Exception {
dataInputMapper.mapDataInput(json);
dataInputRepository.save(dataInput);
}
public DataInput getDataInput() {
return dataInput;
}
public void setDataInput(DataInput dataInput) {
this.dataInput = dataInput;
}
}

How to use #Autowired in EntityListener class?

I have a class DatabaseInitializer which inserts some data via crudrepositories into my database. Now I added an EntityListener which should update a number in another table (table 2), if the date of the entity is not present in table 2. For this I try #Autowired with the crudrepository for this entity. But the repository isn't autowired correctly, it's always null.
The EntityListener:
#Component
public class OrderDayIdListener {
#Autowired
private static OrderRepository orderRepository;
#Autowired
private OrderDayIdRepository orderDayIdRepository;
#PrePersist
private void incrementOrderIdInTable(Order order) {
LocalDate date = order.getDate();
OrderDayId orderDayIdObject = orderDayIdRepository.findByDate(date);
if(orderDayIdObject == null){
orderDayIdObject = new OrderDayId(1L, date);
} else {
orderDayIdObject.incrementId();
}
Long orderDayId = orderDayIdObject.getId();
order.setOrderDayId(orderDayId);
orderDayIdRepository.save(orderDayIdObject);
orderRepository.save(order);
}
}
The Entity:
#EntityListeners(OrderDayIdListener.class)
#Data
#Entity
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#Column(name ="date")
private LocalDate date;
}
As I know you cannot inject spring managed beans into a JPA EntityListener.
What I have found is to create helper class for do the job:
public final class AutowireHelper implements ApplicationContextAware {
private static final AutowireHelper INSTANCE = new AutowireHelper();
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
/**
* Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired
* are null.
*
* #param classToAutowire the instance of the class which holds #Autowire annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
return;
}
}
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
/**
* #return the singleton instance.
*/
public static AutowireHelper getInstance() {
return INSTANCE;
}}
than just do:
public class OrderDayIdListener {
#Autowired
private OrderRepository orderRepository;
#Autowired
private OrderDayIdRepository orderDayIdRepository;
#PrePersist
public void incrementOrderIdInTable(Order order) {
AutowireHelper.autowire(this, this.orderRepository);
AutowireHelper.autowire(this, this.orderDayIdRepository);
LocalDate date = order.getDate();
OrderDayId orderDayIdObject = orderDayIdRepository.findByDate(date);
if(orderDayIdObject == null){
orderDayIdObject = new OrderDayId(1L, date);
} else {
orderDayIdObject.incrementId();
}
Long orderDayId = orderDayIdObject.getId();
order.setOrderDayId(orderDayId);
orderDayIdRepository.save(orderDayIdObject);
orderRepository.save(order);
}}
full explanation here

Use additional JNDI with Spring Boot

I have an enterprise application set to work with a JNDI. Here's the configuration in my application.properties.
spring.datasource.jndi-name=jdbc/MYPRIMARYDATABASE
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.properties.hibernate.current_session_context_class=jta
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.properties.hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
spring.jackson.serialization-inclusion=NON_NULL
and my Spring main: Application.java
#SpringBootApplication
#EnableScheduling
#EnableJpaRepositories(basePackages = { "it.mypackage.data.access.database" })
#EntityScan(basePackages = { "it.mypackage.data.access.model" })
#ComponentScan(basePackages = { "it.mypackage" })
public class Application extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#Bean
public SessionFactory sessionFactory(#Qualifier("entityManagerFactory") EntityManagerFactory emf) {
return emf.unwrap(SessionFactory.class);
}
}
Inside the package it.mypackage.data.access.database there is a DAO interface and a DAO facade which perform operations on the datasource and in the it.mypackage.data.access.model package where all table entities are declared.
DatabaseDAOFacade.java
#Repository
#Transactional
public class DatabaseDAOFacade implements DatabaseDAOInterface {
private final SessionFactory sf;
#SuppressWarnings("unused")
private EntityManager entityManager;
protected Session getSession() {
return sf.getCurrentSession();
}
#Autowired
public DatabaseDAOFacade(SessionFactory sf, EntityManager entityManager) {
this.sf = sf;
this.entityManager = entityManager;
}
#SuppressWarnings("unchecked")
public <T> T save(T o) {
return (T) this.getSession().save(o);
}
.
.
}
Here I have a new entity which should use an additional JNDI
Photo.java
#Entity
#Table(name = "T_PHOTO")
public class Photo {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "PHOTO_REQUEST_SEQ_GEN")
#SequenceGenerator(name = "PHOTO_REQUEST_SEQ_GEN", sequenceName = "PHOTO_REQUESTS")
private Long id;
#Column(name = "USERID")
private String userId;
#Column(name = "UPLOAD_DATE")
private Date uploadDate;
.
.
.
}
Is there a clean way to add a second JNDI and set Photo.java to use the new one?

Spring boot JPA null pointer exception

I am developing an JavaFx application with spring boot,JPA, and H2. I have a user entity when I try to add a new user into the DB it throws NPE in the controller on the button's click action. As it is seen I use only autowire notation. I researched
but findings did not help out. Any help please?
package com.core;
#SpringBootApplication
#Import(SharedSpringConfiguration.class)
public class Runner extends Application {
private ConfigurableApplicationContext context;
public static void main(String[] args) {
launch(args);
}
#Override
public void init() {
context = SpringApplication.run(Runner.class);
}
}
package com.dao;
#Entity
#Table(name = "user")
public class User {
#Id
#Column(name = "id", updatable = false, nullable = false)
private long ID;
#Column(nullable = false)
private String userName;
#Column(nullable = false)
private String userPass;
public User() {
}
public User(long ID, String userName, String userPass) {
this.ID = ID;
this.userName = userName;
this.userPass = userPass;
}
}
package com.service;
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public UserService() {
}
public void saveUser(User user) {
userRepository.save(user);
}
}
package com.repository;
public interface UserRepository extends CrudRepository<User, Long> {}
package com.controller
#Controller
public class MethodController implements Initializable {
#Autowired
private UserService userService;
#FXML
void methodSave(MouseEvent event) {
userService.saveUser(new User(11, "TestUser", "noPass")); //Throws NPE. Indicates that userService is null. But I autowire the userService.
}
}
I don't know what's in SharedSpringConfiguration, but you probably need #EnableJpaRepositories on one of your configuration classes. #Repository on the CrudRepo should be unnecessary.
Change your SpringBootApplication package from com.core to com
because SpringBootApplication by default will scan only that packages and sub packages.
else
add #ComponentScan annotation in SpringBootApplication and scan the packages.

Categories

Resources