Entity listener can inject other Spring dependencies but not repository - spring-boot

I have this entity listener class:
#Component
public class AssignmentListener {
private KafkaService kafkaService;
private String topic;
private AssignmentMapper assignmentMapper;
private AttachmentRepository attachmentRepository;
#Autowired
public final void setKafkaService(KafkaService kafkaService) {
this.kafkaService = kafkaService;
}
#Autowired
public final void setTopic(
#Value("${topic}") String topic
) {
this.topic = topic;
}
#Autowired
public final void setAssignmentMapper(AssignmentMapper assignmentMapper) {
this.assignmentMapper = assignmentMapper;
}
#Autowired
public final void setAttachmentRepository(AttachmentRepository attachmentRepository) {
this.attachmentRepository = attachmentRepository;
}
#PostPersist
#PostUpdate
#Transactional("transactionManager")
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void postUpdate(Assignment assignment) {
var attachments = attachmentRepository.findAllByAssignmentId(assignment.getId());
var dto = assignmentMapper.mapToKafkaMessage(assignment);
dto.setAttachments(
attachments.stream()
.map(Attachment::getPath)
.collect(Collectors.toSet())
);
kafkaService.sendMessage(
topic,
dto
);
}
}
and it worked normally until adding this last field which is repository. All other dependencies were injected however no matter what I do this won't get injected. Just to mention this is happening in tests. Do you have any suggestion?

Related

How to test GET request with body in Spring RestController?

I have a rest controller like this;
#RestController
#RequiredArgsConstructor
#RequestMapping(PO)
public class PoController {
private final PoService service;
#GetMapping(value = FILTER, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<List<PoDTO>> filter(PoFilterCriteria poFilterCriteria) {
return ok().body(service.getPos(poFilterCriteria));
}
}
And I want to write an unit test for it but I couldn't achieve to mock the service to return list.
This is my poFilterCriteria model;
#Data
public class PoFilterCriteria {
private double hp;
private FilterOperationType hpOperationType;
private double attack;
private FilterOperationType attackOperationType;
private double defense;
private FilterOperationType defenseOperationType;
}
And this is my test;
#WebMvcTest(value = PoController.class)
class PoControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private PoService service;
private PoDTO poDTO;
private List<PoDTO> poDTOList;
#BeforeEach
void setUp() {
poDTOList = new ArrayList<>();
poDTO = new Po();
poDTOList.add(poDTO);
}
#Test
public void filter_success() throws Exception {
PoFilterCriteria poFilterCriteria= new PoFilterCriteria ();
poFilterCriteria.setAttack(40);
poFilterCriteria.setAttackOperationType(GT);
poFilterCriteria.setHp(49);
poFilterCriteria.setHpOperationType(EQ);
poFilterCriteria.setDefense(60);
poFilterCriteria.setDefenseOperationType(LT);
when(service.getPos(poFilterCriteria)).thenReturn(poDTOList);
mockMvc.perform(get(PO + FILTER)
.param("hp", String.valueOf(40))
.param("hpOperationType", String.valueOf(GT))
.param("attack", String.valueOf(49))
.param("attackOperationType", String.valueOf(EQ))
.param("defense", String.valueOf(60))
.param("defenseOperationType", String.valueOf(LT))
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(new ObjectMapper().writeValueAsString(poDTOList)));
}
}
But the list that should return with size of 1 is returning empty.
What did I do wrong?
org.mockito.ArgumentMatchers#any(java.lang.Class)
when(service.getPos(any(PoFilterCriteria.class))).thenReturn(poDTOList);
or
org.mockito.ArgumentMatchers#same
when(service.getPos(same(poFilterCriteria))).thenReturn(poDTOList);

How to handle objects created within the method under test

I have the following model classes:
#Data
public class Address {
private String street;
private int number;
}
#Data
public class Person {
private String name;
private Address address;
}
and the following services:
#Service
public class MyService {
private final OtherService otherService;
public MyService(OtherService otherService) {
this.otherService = otherService;
}
public void create() {
Person myPerson = new Person();
myPerson.setName("John");
otherService.synchronize(myPerson);
myPerson.getAddress().setNumber(12);
}
}
#Service
public class OtherService {
public void synchronize(Person person) {
Address address = new Address();
address.setStreet("sample street");
address.setNumber(123);
person.setAddress(address);
}
}
I want to write a unit test for MyService. This is the not working version of the test:
#ExtendWith(SpringExtension.class)
class MyServiceTest {
#Mock OtherService otherService;
#InjectMocks MyService myService;
#Test
void test_create() {
// GIVEN
doNothing().when(otherService).synchronize(any(Person.class));
// WHEN
myService.create();
// THEN
verify(otherService).synchronize(any());
}
}
This fails because the myPerson object is created within the method being tested and therefore I get a NullPointerException when running the test. How could I deal with this issue? should I capture the value passed to the otherService?
There's a little complexity but it's not bad. Replace your doNothing call with something like this:
Mockito.doAnswer(
new Answer<Void>() {
public Void answer(InvocationOnMock invocation) throws Exception {
Person arg = invocation.getArgument(0);
arg.setAddress(new Address());
return;
}
}).when(otherService).synchronize(any(Person.class));

java.lang.ClassCastException: Entity A incompatible with Entity B

I'm trying to get proficient in generics in Java. I have some 100 entities that use the same findBy method in JPA interface. Almost all of them require a call to AwrSnapDetails so instead of adding
#ManyToOne private AwrSnapDetails awrSnapDetails; to each Entity, I've created a HelperEntity class and using #Embedded annotation. Now I have gotten to the point in coding where I can't figure out what I am doing wrong and how to go about resolving this error.
Entity
#Entity
public class AwrMemStats {
String description;
double begin_;
double end_;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Embedded
private HelperEntity helperEntity;
public AwrMemStats() {
}
public AwrMemStats(String description, double begin_, double end_, AwrSnapDetails awrSnapDetails) {
this.description = description;
this.begin_ = begin_;
this.end_ = end_;
HelperEntity h = new HelperEntity(awrSnapDetails);
}
// getters/setters removed for clarity
}
Embedded Entity
#Embeddable
public class HelperEntity implements Serializable{
private static final long serialVersionUID = 1L;
#ManyToOne
AwrSnapDetails awrSnapDetails;
public HelperEntity() {
}
public HelperEntity(AwrSnapDetails awrSnapDetails) {
super();
this.awrSnapDetails = awrSnapDetails;
}
public AwrSnapDetails getAwrSnapDetails() {
return awrSnapDetails;
}
public AwrSnapDetails setAwrSnapDetails(AwrSnapDetails awrSnapDetails) {
return this.awrSnapDetails = awrSnapDetails;
}
}
Service Class
#Service
public class HelperService<T> {
#Autowired
private HelperRepository<T> repository;
public void add(T entity) {
repository.save(entity);
}
public void add(List<T> entities) {
repository.saveAll(entities);
}
public T get(T entity) {
T t = repository.findByHelperEntityAwrSnapDetailsStartSnapIdAndHelperEntityAwrSnapDetailsInstanceDetailDbNameAndHelperEntityAwrSnapDetailsInstanceDetailDbId(
((HelperEntity) entity).getAwrSnapDetails().getStartSnapId(),
((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbName(),
((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbId());
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getStartSnapId(),
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbName(),
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbId());
if (t!= null) {
return t;
}
return null;
}
}
Controller
#RestController
public class HelperController<T> {
#Autowired
private HelperService<T> service;
public void add(T entity) {
service.add(entity);
}
public void add(List<T> entities) {
service.add(entities);
}
public T get(T entity) {
return service.get(entity);
}
}
Execution
getAwrSnapDetails() initilized in HelperLoader
#Component
public class LoadAwrMemStats extends HelperLoader{
#Autowired
private HelperController<AwrMemStats> controller;
public void doThis() {
AwrMemStats profile = new AwrMemStats("a",1.0,1.0,getAwrSnapDetails());
AwrMemStats s = controller.get(profile);
ANd finally the ERROR message
Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
...
...
Caused by: java.lang.ClassCastException: net.mharoon.perfmon.awr.entities.AwrMemStats incompatible with net.mharoon.perfmon.awr.entities.HelperEntity
at net.mharoon.perfmon.awr.service.HelperService.get(HelperService.java:27)
at net.mharoon.perfmon.awr.controller.HelperController.get(HelperController.java:24)
...
...
Update this code works but only for given class AwrMemStats.
public List<T> get(T entity) {
List<T> ts = repository.findByHelperEntityAwrSnapDetailsStartSnapIdAndHelperEntityAwrSnapDetailsInstanceDetailDbIdAndHelperEntityAwrSnapDetailsInstanceDetailDbName(
//((HelperEntity) entity).getAwrSnapDetails().getStartSnapId(),
//((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbName(),
//((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbId());
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getStartSnapId(),
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbId(),
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbName());
if (!ts.isEmpty()) {
return ts;
}
return null;
}
The reason is because you are returning an Object that is not AwrMemStats and assigning it to AwrMemStats.
A simple work around is to replace
public T get(T entity)
with
public <T extends AwrMemStats> T get(T entity)
EDIT : Another solution (which is more generic) is..
replace
public class AwrMemStats
with
public class AwrMemStats extends HelperEntity
then replace
AwrMemStats s = controller.get(profile);
with
AwrMemStats s = (AwrMemStats) controller.get(profile);

Spring Boot class cast exception in PostConstruct method

I am running a Spring Boot application with a PostConstruct method to populate a POJO before application initialization. This is to ensure that the database isn't hit by multiple requests to get the POJO content after it starts running.
I'm able to pull the data from Oracle database through Hibernate query and store it in my POJO. The problem arises when I try to access the stored data. The dataset contains a list of objects that contain strings and numbers. Just trying to print the description of the object at the top of the list raises a class cast exception. How should I mitigate this issue?
#Autowired
private TaskDescrBean taskBean;
#PostConstruct
public void loadDescriptions() {
TaskDataLoader taskData = new TaskDataLoader(taskBean.acquireDataSourceParams());
List<TaskDescription> taskList = tdf.getTaskDescription();
taskBean.setTaskDescriptionList(taskList);
System.out.println("Task description size: " + taskBean.getTaskDescriptionList().get(0).getTaskDescription());
}
My POJO class:
#Component
public class TaskDescrBean implements ApplicationContextAware {
#Resource
private Environment environment;
protected List<TaskDescription> taskDescriptionList;
public Properties acquireDataSourceParams() {
Properties dataSource = new Properties();
dataSource.setProperty("hibernate.connection.driver_class", environment.getProperty("spring.datasource.driver-class-name"));
dataSource.setProperty("hibernate.connection.url", environment.getProperty("spring.datasource.url"));
dataSource.setProperty("hibernate.connection.username", environment.getProperty("spring.datasource.username"));
dataSource.setProperty("hibernate.connection.password", environment.getProperty("spring.datasource.password"));
return dataSource;
}
public List<TaskDescription> getTaskDescriptionList() {
return taskDescriptionList;
}
public void setTaskDescriptionList(List<TaskDescription> taskDescriptionList) {
this.taskDescriptionList = taskDescriptionList;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
My DAO class:
public class TaskDataLoader {
private Session session;
private SessionFactory sessionFactory;
public TaskDataLoader(Properties connectionProperties) {
Configuration config = new Configuration().setProperties(connectionProperties);
config.addAnnotatedClass(TaskDescription.class);
sessionFactory = config.buildSessionFactory();
}
#SuppressWarnings("unchecked")
public List<TaskDescription> getTaskDescription() {
List<TaskDescription> taskList = null;
session = sessionFactory.openSession();
try {
String description = "from TaskDescription des";
Query taskDescriptionQuery = session.createQuery(description);
taskList = taskDescriptionQuery.list();
System.out.println("Task description fetched. " + taskList.getClass());
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
return taskList;
}
TaskDescription Entity:
#Entity
#Table(name="TASK_DESCRIPTION")
#JsonIgnoreProperties
public class TaskDescription implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="TASK_DESCRIPTION_ID")
private Long taskDescriptionId;
#Column(name="TASK_DESCRIPTION")
private String taskDescription;
public Long getTaskDescriptionId() {
return taskDescriptionId;
}
public void setTaskDescriptionId(Long taskDescriptionId) {
this.taskDescriptionId = taskDescriptionId;
}
public String getTaskDescription() {
return taskDescription;
}
public void setTaskDescription(String taskDescription) {
this.taskDescription = taskDescription;
}
}
StackTrace
Instead of sending the List in the return statement, I transformed it into a JSON object and sent its String representation which I mapped back to the Object after transforming it using mapper.readValue()

Generate static map from database using a singleton class also using spring configuration #Autowired

I need to create an unmodifiable map generated from data obtained by querying a database. How, or can I, or is there a better way to do this using spring annotations?
I ran into a problem when creating a singleton for my Regions class and then trying to #Autowire in a RegionService to grab the object from the DAO. The problem is that spring can't instantiate the RegionService because it needs to instantiate the static singleton class Regions which needs to get data from the database as shown below in the constructor.
Please see me classes below (I've removed multiple unneeded methods that don't pertain to this question):
public final class Region {
private static final String DEFAULT_SEPERATOR = "-";
private final Integer key;
private final String description;
public Region(Integer pKey, String pDescription) {
this.key = pKey;
this.description = pDescription;
}
public Integer getKey() {
return this.key;
}
public String getValue() {
return this.description;
}
}
Here is my singleton:
public final class Regions {
private static Regions regionsInstance = null;
#Autowired
private RegionService regionService;
static Map<Integer, Region> regions;
private Regions() {
final Map<Integer, Region> tempRegions = new HashMap<Integer, Region>();
for (final Region region : this.regionService.retrieveAll()) {
tempRegions.put(region.getKey(), region);
}
regions = Collections.unmodifiableMap(tempRegions);
}
public static synchronized Regions getRegionsInstance() {
if (regionsInstance == null) {
regionsInstance = new Regions();
}
return regionsInstance;
}
public Region getRegion(final Integer pKey) {
return regions.get(pKey);
}
public List<Region> getRegions() {
return (List<Region>) regions.values();
}
}
My DAO and Service are just interfaces, no need to post those, here are my Impls:
#Service
public class RegionServiceImpl implements RegionService {
#Autowired
private RegionDAO regionDao;
#Override
public List<Region> retrieveAll() {
return this.regionDao.retrieveAll();
}
}
My DAOImpl (tested and works, just posting to give you the full picture):
#Repository
public class RegionDAOImpl implements RegionDAO {
private static final String SQL_RETRIEVE_REGIONS = "some random SQL";
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public List<Region> retrieveAll() {
try {
return this.jdbcTemplate.query(SQL_RETRIEVE_REGIONS, new ResultSetExtractor<List<Region>>() {
#Override
public List<Region> extractData(ResultSet rs) throws SQLException, DataAccessException {
return RegionDAOImpl.this.mapRegionData(rs);
}
});
} catch (final DataAccessException dae) {
throw new DaoException("Could not retrieve regionList from database. " + dae);
}
}
protected final List<Region> mapRegionData(ResultSet rs) throws SQLException {
final List<Region> regionList = new ArrayList<Region>();
while (rs.next()) {
regionList.add(new Region(rs.getInt("REGION_CD"), rs.getString("REGION_TXT")));
}
return Collections.unmodifiableList(regionList);
}
}
Then I run my test(I took out unneeded crap):
#..annotated with things you don't need to know
public class RetrieveRegionsTest {
#Autowired
private Regions r;
#Test
public void getAndLogRegion() {
final List<Region> regionDescriptions = new ArrayList<Region>(this.r.getRegions());
for (final Region region : regionDescriptions) {
LOGGER.info(region.getValue());
}
}
Yes my configuration and classpaths are set up properly. I can get this to work other ways, just not by accessing the Regions singleton which is what I want. Now I know I could take off the #Autowired on the RegionService in my Regions singleton and just create a new instance of RegionService, but that would defeat the purpose of springs #Autowired feature.
Any thoughts, ideas, comments?

Resources