How to retrieve the repository from JHipster spring controller? - spring

I have a JHipster microservice application, and I've added a spring controller. However, it is generated without a repository and I don't know how to retrieve it to perform data tasks.
This is the code:
#RestController
#RequestMapping("/api/data")
public class DataResource {
private final Logger log = LoggerFactory.getLogger(DataResource.class);
private final DeviceRepository deviceRepository;
public DataResource() {
}
/**
* GET global
*/
#GetMapping("/global")
public ResponseEntity<GlobalStatusDTO[]> global() {
List<Device> list=deviceRepository.findAll();
GlobalStatusDTO data[]=new GlobalStatusDTO[]{new GlobalStatusDTO(list.size(),1,1,1,1)};
return ResponseEntity.ok(data);
}
}
EDIT: I need to inject an already existing repository, here is the CRUD part where the repository is initialized:
#RestController
#RequestMapping("/api")
#Transactional
public class DeviceResource {
private final Logger log = LoggerFactory.getLogger(DeviceResource.class);
private static final String ENTITY_NAME = "powerbackDevice";
#Value("${jhipster.clientApp.name}")
private String applicationName;
private final DeviceRepository deviceRepository;
public DeviceResource(DeviceRepository deviceRepository) {
this.deviceRepository = deviceRepository;
}
/**
* {#code POST /devices} : Create a new device.
*
* #param device the device to create.
* #return the {#link ResponseEntity} with status {#code 201 (Created)} and with body the new device, or with status {#code 400 (Bad Request)} if the device has already an ID.
* #throws URISyntaxException if the Location URI syntax is incorrect.
*/
#PostMapping("/devices")
public ResponseEntity<Device> createDevice(#Valid #RequestBody Device device) throws URISyntaxException {
...

I might misunderstand you, but your first code part doesn't work, because, you didn't inject DeviceRepository by the constructor. Of course, there are other methods of injections.
#RestController
#RequestMapping("/api/data")
public class DataResource {
private final Logger log = LoggerFactory.getLogger(DataResource.class);
private final DeviceRepository deviceRepository;
//changes are here only, constructor method of injection
public DataResource(DeviceRepository deviceRepository) {
this.deviceRepository = deviceRepository;
}
/**
* GET global
*/
#GetMapping("/global")
public ResponseEntity<GlobalStatusDTO[]> global() {
List<Device> list=deviceRepository.findAll();
GlobalStatusDTO data[]=new GlobalStatusDTO[]{new GlobalStatusDTO(list.size(),1,1,1,1)};
return ResponseEntity.ok(data);
}
}

Related

Get Entity Class in Generic Spring Data Repository Fragment

I'm trying to add some custom functionality to a Spring Data Repository using the Repository composing features documented here
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behavior
Specifically, I'm trying to create a generic interface and implementation I can use with many of my repositories which seem to be a valid approach given "Example 34. Fragments overriding" in the link above which shows just this scenario.
I have got my generic interface DynamicEntityGraph
public interface DynamicEntityGraph<T> {
List<T> findAll(EntityGraph<T> entityGraph);
}
and the corresponding implementation
public class DynamicEntityGraphImpl<T> implements DynamicEntityGraph<T> {
#Autowired
private EntityManager em;
Logger logger = LoggerFactory.getLogger(getClass());
#Override
public List<T> findAll(EntityGraph<T> entityGraph) {
//EM Code Goes Here
return null;
}
}
which I then use in a repository like
#Repository
public interface SiteRepository extends CustomerScopedRepository<Site>, DynamicEntityGraph<Site> {
}
This part all works fine.
My issue is in the DynamicEntityGraphImpl I need to get the Class of T at runtime in order to create my Entity Manager queries. After extensive research, I've not been able to find a solution in which I can obtain the Class of T, despite it looking like a valid solution given it exists in the Spring Data JPA Docs.
I've read the source code of JpaRepositoryFactory and can see the class is injected into the repository when it's constructed, but I'm not sure how to access this from the fragment.
Any assistance would be greatly appreciated.
For my future self or other lost travellers on a similar path, I figured it out and here is the solution.
A similar question was asked on the Spring Data project and this clears up that fragments are actually initialised as singletons in the Spring context so it's not possible to pass the EntityInformation to them.
Note: If you're not trying to make a generic fragment then this isn't the right solution for you.
The solution is to then extend the JpaRepositoryFactory and JpaRepositoryFactoryBean with a little extra configuration.
public class MyJpaRepositoryFactory extends JpaRepositoryFactory {
private EntityManager entityManager;
public MyJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
#Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryFragments fragments = super.getRepositoryFragments(metadata);
if(DynamicEntityGraph.class.isAssignableFrom(metadata.getRepositoryInterface()))
{
JpaEntityInformation<?, Serializable> entityInformation = this.getEntityInformation(metadata.getDomainType());
fragments = fragments.append(RepositoryFragment.implemented(DynamicEntityGraph.class, new DynamicEntityGraphImpl(entityInformation, entityManager)));
}
return fragments;
}
}
this simply checks if the repository being instantiated implements our interface and if it does we construct a new instance of our class, passing it the EntityInformation and EntityManger.
public class MyJpaRepositoryFactoryBean<T extends JpaRepository<Object, Serializable>> extends JpaRepositoryFactoryBean<T , Object, Serializable> {
public MyJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
#Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyJpaRepositoryFactory(entityManager);
}
}
and finally, we tell Spring Data to use this as our factory bean
#SpringBootApplication
#EnableJpaRepositories(repositoryFactoryBeanClass = MyJpaRepositoryFactoryBean.class)
public class ApplicationApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApplicationApiApplication.class, args);
}
}
It's important to also mark your fragment interface as #NoRepositoryBean to prevent Spring from making a bean from it.
#NoRepositoryBean
public interface DynamicEntityGraph<T> {
List<T> findAll(EntityGraph<T> entityGraph);
}
I hope this saves you some time!
I am using a utility object SubGraphUtil. SubGraphUtil is used to build the graph.
I also have a specification object for the where part
public abstract class AbstractCustomRepository<T> {
private static final String FETCH_GRAPH = "javax.persistence.fetchgraph";
#PersistenceContext
protected EntityManager entityManager;
#Setter
private Class<T> entityClass;
/**
* Permet de recuperer une liste d entites en choisissant le graphe et la specification
*
* #param subGraphUtilList
* #param specification
* #return une liste d entites en choisissant le graphe
*/
public List<T> findAllWithGraphAndSpecification(List<SubGraphUtil> subGraphUtilList,
#Nullable Specification<T> specification) {
EntityGraph<T> graph = buildEntityGraphFromUtil(subGraphUtilList);
CriteriaQuery<T> criteriaQuery = getCriteriaQuery(specification);
return entityManager.createQuery(criteriaQuery)
.setHint(FETCH_GRAPH, graph)
.getResultList();
}
/**
* Permet de construire EntityGraph
*
* #param subGraphUtilList
* #return EntityGraph
*/
private EntityGraph<T> buildEntityGraphFromUtil(List<SubGraphUtil> subGraphUtilList) {
EntityGraph<T> graph = entityManager.createEntityGraph(entityClass);
if (isNotEmpty(subGraphUtilList)) {
subGraphUtilList.forEach(
subGraphUtil -> {
Subgraph<T> subgraph = graph.addSubgraph(subGraphUtil.getName());
if (isNotEmpty(subGraphUtil.getSubGraphUtilList())) {
subGraphUtil.getSubGraphUtilList()
.forEach(
childGraph -> addSubGraph(subgraph, childGraph)
);
}
}
);
}
return graph;
}
}
Example of use in a repository
public class ContratCustomRepositoryImpl extends AbstractCustomRepository<Contrat>
implements ContratCustomRepository {
public ContratCustomRepositoryImpl() {
super();
setEntityClass(Contrat.class);
}
}
public interface ContratCustomRepository {
List<Contrat> findAllWithGraphAndSpecification(List<SubGraphUtil> subGraphUtilList,
Specification<Contrat> specification);
}
Full code: Here

Hikari connection is unavailable

I am encountering an issue where a Spring Boot service, after running for about a day with some load, suddenly is unable to create a connection.
I used the standard Spring Boot 2 project starter. Created a rest controller, a model, a service, and a repository.
#Entity
#Table(name = "partners")
public class Partner {
/** Unique identifier. */
#Id
#JsonProperty("id")
private long id;
/** Partner name. */
#JsonProperty("name")
private String name;
/**
* Default constructor. Not usable publicly.
*/
protected Partner() {
}
// etc...
}
Repo
/**
* Partner repo.
*/
public interface PartnerRepository extends CrudRepository<Partner, Long> {
}
The Service
#Service
public class PartnersService {
/** Access to Partner repo. */
#Autowired
private PartnerRepository partnerRepo;
/**
* Get partner data.
*
* #param id Partner identifier.
* #return Partner instance, or null if not found.
*/
public Partner getPartner(final long id) throws ServiceException {
if (id <= 0) {
throw new ServiceException("Please enter a valid partner identifier.");
}
final Optional<Partner> partner = partnerRepo.findById(id);
return partner.orElse(null);
}
}
The Controller
#RestController
public class PartnerController extends BaseController {
/** Partners service. */
#Autowired
private PartnersService partnersService;
#RequestMapping(value = "/partners/api/v1/partners/{id}", method = GET)
public ResponseEntity<FilldResponse> getPartner(#PathVariable final long id) {
final FilldResponse response = new FilldResponse();
HttpStatus httpStatus = HttpStatus.OK;
try {
final Partner partner = partnersService.getPartner(id);
if (partner != null) {
response.setData(partner);
response.setMessage("Partner retrieved successfully.");
} else {
response.setMessage("Partner not found.");
response.setStatusCode(4000);
response.setStatus(false);
httpStatus = HttpStatus.NOT_FOUND;
}
} catch (Exception ex) {
response.setStatus(false);
response.setMessage(ex.getMessage());
response.setStatusCode(4000);
httpStatus = HttpStatus.BAD_REQUEST;
}
return new ResponseEntity<>(response, httpStatus);
}
}
Everything is pretty much default configuration. After a while of use in production, I get:
Mar 09 09:18:22 52.43.134.45-1 partners: 2020-03-09 16:18:22.449 WARN 1 --- [nio-8080-exec-9] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 08S01
Mar 09 09:18:22 52.43.134.45-1 partners: 2020-03-09 16:18:22.449 ERROR 1 --- [nio-8080-exec-9] o.h.engine.jdbc.spi.SqlExceptionHelper : HikariPool-1 - Connection is not available, request timed out after 30000ms.
This is running in AWS as a docker image.
Are there known issues or configuration that is not configured out of the box that might be causing this?

What are the best practice for audit log(user activity) in micro-services?

In our microservice architecture, we are logging user-activity to mongo database table? Is there any good way to store and retrieve audit log?
You can think of a solution something similar to the below by storing AuditLogging into the Mongo db by using DAO pattern.
#Entity
#Table(name = "AuditLogging")
public class AuditLogging implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "auditid", updatable = false, nullable = false)
private Long auditId;
#Column(name = "event_type", length = 100)
#Enumerated(EnumType.STRING)
private AuditingEvent event;
#Column(name = "event_creator", length = 100)
#Enumerated(EnumType.STRING)
private EventCreator eventCreator;
#Column(name = "adminid", length = 20)
private String adminId;
#Column(name = "userid", length = 20)
private String userId;
#Column(name = "event_date")
private Date eventDate;
}
public class Constants {
public static final String EVENT_TYPE = "eventType";
public static final String EVENT_CREATOR = "eventCreator";
public static final String NEW_EMAIL_ID = "newEmailId";
public static final String OLD_EMAIL_ID = "oldEmailId";
}
public enum AuditEvent {
USER_REGISTRATION,
USER_LOGIN,
USER_LOGIN_FAIL,
USER_ACCOUNT_LOCK,
USER_LOGOFF,
USER_PASSWORD_CHANGE,
USER_PASSWORD_CHANGE_FAIL,
USER_FORGOT_PASSWORD,
USER_FORGOT_PASSWORD_FAIL,
ADMIN_LOGIN
}
public enum EventCreator {
ADMIN_FOR_SELF,
USER_FOR_SELF,
ADMIN_FOR_USER
}
public interface AuditingDao {
/**
* Stores the event into the DB/Mongo or Whatever
*
* #param auditLogging
* #return Boolean status
*/
Boolean createAuditLog(final AuditLogging auditLogging);
/* Returns the log occurrence of a specific event
*
* #param event
* #return List of logged events by type
*/
List<AuditLogging> getLogsForEvent(final AuditingEvent event);
}
public interface AuditingService {
/**
* Creates an Audit entry in the AuditLogging table using the
* DAO layer
*
* #param auditingEvent
* #param eventCreator
* #param userId
* #param adminId *
* #return {#link Boolean.TRUE} for success and {#link Boolean.FALSE} for
* failure
*/
Boolean createUserAuditEvent(final AuditEvent auditEvent,
final EventCreator eventCreator, final String userId, final String adminId,
final String newEmailId,final String oldEmailId);
/**
*
* Returns all event for a user/admin based on the id
*
* #param id
* #return List of logged events for an id
*/
List<AuditLogging> fetchLoggedEventsById(final String id);
/***
* Returns all event based on event type
*
* #param eventName
* #return List of logged events for an event
*/
List<AuditLogging> fetchLoggedEventsByEventName(final String eventName);
}
#Service("auditingService")
public class AuditServiceImpl implements AuditingService {
#Autowired
private AuditingDao auditingDao;
private static Logger log = LogManager.getLogger();
#Override
public Boolean createUserAuditingEvent(AuditEvent auditEvent,
EventCreator eventCreator, String userId, String adminId,
String newEmailId,String oldEmailId) {
AuditLogging auditLogging = new AuditLogging();
auditLogging.setEvent(auditingEvent);
auditLogging.setEventCreator(eventCreator);
auditLogging.setUserId(userId);
auditLogging.setAdminId(adminId);
auditLogging.setEventDate(new Date());
return Boolean.TRUE;
}
#Override
public List<AuditLogging> fetchLoggedEventsByEventName(
final String eventName) {
AuditEvent event = null;
try {
event = AuditingEvent.valueOf(eventName);
} catch (Exception e) {
log.error(e);
return Collections.emptyList();
}
return auditingDao.getLogsForEvent(event);
}
public void setAuditingDao(AuditingDao auditingDao) {
this.auditingDao = auditingDao;
}
}
Writing an aspect is always good for this type of scenarios by pointing to the appropriate controller method to trigger the event.
#Aspect
#Component("auditingAspect")
public class AuditingAspect {
#Autowired
AuditingService auditingService;
/* The below controllers you can think of your microservice endpoints
*/
#Pointcut("execution(* com.controller.RegistrationController.authenticateUser(..)) ||execution(* com.controller.RegistrationController.changeUserPassword(..)) || execution(* com.controller.RegistrationController.resetPassword(..)) ||execution(* com.controller.UpdateFunctionalityController.updateCustomerDetails(..))")
public void aroundPointCut() {}
#Around("aroundPointCut()")
public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint)
throws Throwable {
joinPoint.getSignature().getName();
joinPoint.getArgs();
// auditingService
Object result = joinPoint.proceed();
ResponseEntity entity = (ResponseEntity) result;
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
if(!((request.getAttribute(Constants.EVENT_TYPE).toString()).equalsIgnoreCase(AuditEvent.USER_LOGIN.toString()) || (((request.getAttribute(Constants.EVENT_TYPE).toString()).equalsIgnoreCase(AuditEvent.ADMIN_LOGIN.toString()))))){
auditingService.createUserAuditEvent(
(AuditingEvent) request.getAttribute(Constants.EVENT_TYPE),
(EventCreator) request.getAttribute(Constants.EVENT_CREATOR),
(request.getAttribute(Constants.USER_ID)!= null ? request.getAttribute(Constants.USER_ID).toString():""), null,
(request.getAttribute(Constants.NEW_EMAIL_ID) == null ? ""
: request.getAttribute(Constants.NEW_EMAIL_ID).toString()),
(request.getAttribute(Constants.OLD_EMAIL_ID) == null ? ""
: request.getAttribute(Constants.OLD_EMAIL_ID).toString()));
}
return entity;
}
}
From the REST controller the Aspect will be triggered when it finds the corresponding event.
#RestController
public class RegistrationController {
#RequestMapping(path = "/authenticateUser", method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
/* This method call triggers the aspect */
#ResponseBody
public ResponseEntity<String> authenticateUser(HttpServletRequest request, #RequestBody User user)
throws Exception {
request.setAttribute(Constants.EVENT_TYPE, AuditingEvent.USER_LOGIN);
request.setAttribute(Constants.EVENT_CREATOR, EventCreator.USER_FOR_SELF);
request.setAttribute(Constants.USER_ID, user.getUserId());
ResponseEntity<String> responseEntity = null;
try {
// Logic for authentication goes here
responseEntity = new ResponseEntity<>(respData, HttpStatus.OK);
} catch (Exception e) {
request.setAttribute(Constants.EVENT_TYPE, AuditEvent.USER_LOGIN_FAIL);
responseEntity = new ResponseEntity<>(respData, HttpStatus.INTERNAL_SERVER_ERROR);
}
return responseEntity;
}
}
I hope this answer make sense and you can implement similar functionality for Mongo as well.
Cheers !

JSF 2 managed bean is being shared across mulitple users logged in different window

I am using JSF2, Spring 3 and Mybatis. On user login, I am doing authentication from ConfigAutomationLoginBean.java which has other beans as its Managed properties. The problem is that my beans are being shared across multiple users in different browser window. All my beans are SessionScoped and if I don't keep it that way, I may not get navigation screen after login. I guess JSF creates all managed bean with SessionScoped attribute by default singleton. Would it be good idean if I make the initial bean authentication bean ie. ConfigAutomationLoginBean and other beans autorwired to it using Spring 3 and remove the JSF for intial bean loading? Below is my login code:
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.telus.routerconfigurationtool.dto.UserProfileDTO;
import com.telus.routerconfigurationtool.service.UserManagementService;
import com.telus.routerconfigurationtool.util.CommonUtil;
#Component
#ManagedBean(name = "configAutomationLoginBean")
#SessionScoped
public class ConfigAutomationLoginBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(ConfigAutomationLoginBean.class);
private String id;
private String password;
private String role;
#ManagedProperty(value = "#{userManagementService}")
private UserManagementService userManagementService;
#ManagedProperty(value = "#{treeNavigationBean}")
private TreeNavigationBean treeNavigationBean;
#ManagedProperty(value = "#{breadCrumbBean}")
private BreadCrumbBean breadCrumbBean;
public String authenticateUser() {
/** Reset and Update BreadCrumb - Add Nodes for Create User **/
breadCrumbBean.resetBreadCrumbModel();
Boolean authenticUser = false;
//TODO logic to authenticate user. authenticUser set true if authentication
//TODO Temporary setting to true
authenticUser = true;
if (authenticUser) {
return authorizeUser();
} else {
CommonUtil.displayFacesMessage(this.getClass(), FacesContext.getCurrentInstance(),
FacesMessage.SEVERITY_ERROR, "ERR1", id);
return "index";
}
}
private String authorizeUser() {
UserProfileDTO userProfileDTO = new UserProfileDTO();
CommonUtil.copyProperties(userProfileDTO, this);
Boolean authorizedUser = false;
// logic to authorize user. authorizedUser set true if authorization is
// successful
authorizedUser = userManagementService.authorizeUser(userProfileDTO);
if (authorizedUser) {
// Set User Role fetched from Database
this.role = userProfileDTO.getRole();
treeNavigationBean.setLoggedInUserId(id);
treeNavigationBean.setLoggedInUserRole(role);
treeNavigationBean.createTreeByUserRole();
treeNavigationBean.setViewCenterContent(null);
return "treeNavigation";
} else {
// Display Error Message that user is not authorized.
CommonUtil.displayFacesMessage(this.getClass(), FacesContext.getCurrentInstance(),
FacesMessage.SEVERITY_ERROR, "ERR2", id);
return "index";
}
}
/**
* #return the id
*/
public String getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(String id) {
if (StringUtils.isBlank(id)) {
this.id = id;
} else {
this.id = id.toUpperCase();
}
}
/**
* #return the password
*/
public String getPassword() {
return password;
}
/**
* #param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* #return the role
*/
public String getRole() {
return role;
}
/**
* #param role the role to set
*/
public void setRole(String role) {
this.role = role;
}
/**
* #return the userManagementService
*/
public UserManagementService getUserManagementService() {
return userManagementService;
}
/**
* #param userManagementService the userManagementService to set
*/
public void setUserManagementService(UserManagementService userManagementService) {
this.userManagementService = userManagementService;
}
/**
* #return the treeNavigationBean
*/
public TreeNavigationBean getTreeNavigationBean() {
return treeNavigationBean;
}
/**
* #param treeNavigationBean the treeNavigationBean to set
*/
public void setTreeNavigationBean(TreeNavigationBean treeNavigationBean) {
this.treeNavigationBean = treeNavigationBean;
}
/**
* #return the breadCrumbBean
*/
public BreadCrumbBean getBreadCrumbBean() {
return breadCrumbBean;
}
/**
* #param breadCrumbBean the breadCrumbBean to set
*/
public void setBreadCrumbBean(BreadCrumbBean breadCrumbBean) {
this.breadCrumbBean = breadCrumbBean;
}
}
Note: Since TreeNavigation bean is sessionscoped and single instance is shared, loggedInUserName is changed everytime different user is logging in. If user1 and user 2 logged in, then user1 who logged in first will see the screen of user2.
#ManagedBean(name = "treeNavigationBean")
#SessionScoped
public class TreeNavigationBean implements Serializable {
private static final long serialVersionUID = 1892577430001834938L;
private static final Logger LOGGER = Logger.getLogger(TreeNavigationBean.class);
private TreeNode root;
private TreeNode selectedNode;
private String loggedInUserId;
private String loggedInUserRole;
private String loggedInUserName;
private String viewCenterContent;
private String userAction;
#ManagedProperty(value = "#{userProfileBean}")
private UserProfileBean userProfileBean;
#ManagedProperty(value = "#{createConfigurationBean}")
private CreateConfigurationBean createConfigurationBean;
#ManagedProperty(value = "#{placeholderBean}")
private CreateConfigPlaceholderBean placeholderBean;
#ManagedProperty(value = "#{ncParamMappingBean}")
private NCParamMappingBean ncParamMappingBean;
#ManagedProperty(value = "#{breadCrumbBean}")
private BreadCrumbBean breadCrumbBean;
#ManagedProperty(value = "#{createTemplateBean}")
private CreateTemplateBean createTemplateBean;
#ManagedProperty(value = "#{configurationManagementBean}")
private ConfigurationManagementBean configurationManagementBean;
public void createTreeByUserRole() {
root = new DefaultTreeNode("Root", null);
if (TreeNodesEnum.SUPER_USER.equals(loggedInUserRole)) {
addCreateConfigurationNodes();
addTemplateAdministrationNodes();
addUserAdministrationNodes();
} else if (TreeNodesEnum.ADMIN_USER.equals(loggedInUserRole)) {
addCreateConfigurationNodes();
addTemplateAdministrationNodes();
} else if (TreeNodesEnum.NORMAL_USER.equals(loggedInUserRole)) {
addCreateConfigurationNodes();
}
}
.....................
With #Component you are using spring mvc beans instead of JSF beans. You can either switch to JSF beans or use Spring scopes, for example #Scope("session").

Update Logic for JPA Entity Fails when using DBUnit and Spring

I am currently using DBUnit in conjunction with Spring in order to unit test my application, but have run into an issue where my update logic test always fails because a deadlock occurs on the database and I cannot figure out why this is the case. Please note that I have been able to get around the issue by removing the method annotated by #After, which really isn't needed because I am using the #TransactionConfiguration annotation, but I'm concerned that I'm misunderstanding something regarding how the transaction processing works and thus am hoping someone can indicate why I always get the following exception when running my updateTerritory method.
java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
One thing that may be helpful to point out is that I am able to perform other actions like querying the database and inserting new records without any lock errors. In addition I am using OpenJPA and spring is injecting the PersistenceUnit into my DAO. I'm guessing that mixing up the usage of the PersistenceUnit and the direct use of the datasource within my DBUnit setup code (testSetup and testTeardown) may be part of the issue. I'm currently using Derby as my database.
My Code is provided below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/applicationContext.xml")
#TransactionConfiguration(defaultRollback = true)
public class TerritoryZoneManagerTest {
#Autowired
private DataSource unitTestingDataSource;
#Autowired
private ITerritoryZoneDaoManager mgr;
#Before
public void testSetup() throws DatabaseUnitException, SQLException,
FileNotFoundException {
Connection con = DataSourceUtils.getConnection(unitTestingDataSource);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
IDataSet dataSet = builder
.build(new FileInputStream(
"./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml"));
try {
// NOTE: There is no need to use the DatabaseOperation.DELETE
// functionality because spring will automatically remove all
// inserted records after each test case is executed.
DatabaseOperation.REFRESH.execute(dbUnitCon, dataSet);
} finally {
DataSourceUtils.releaseConnection(con, unitTestingDataSource);
}
}
#After
public void testTeardown() throws DatabaseUnitException, SQLException,
FileNotFoundException {
Connection con = DataSourceUtils.getConnection(unitTestingDataSource);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
IDataSet dataSet = builder
.build(new FileInputStream(
"./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml"));
try {
// NOTE: There is no need to use the DatabaseOperation.DELETE
// functionality because spring will automatically remove all
// inserted records after each test case is executed.
DatabaseOperation.DELETE.execute(dbUnitCon, dataSet);
} finally {
DataSourceUtils.releaseConnection(con, unitTestingDataSource);
}
}
#Test
#Transactional
public void updateTerritory() {
TerritoryZone zone = new TerritoryZone();
int id = 1;
zone = mgr.getTerritory(id);
String newCity = "Congerville";
zone.setCity(newCity);
mgr.updateTerritory(zone);
zone = mgr.getTerritory(id);
Assert.assertEquals(newCity, zone.getCity());
}
}
The DAO object is provided below as well in case that is useful.
#Repository
public class TerritoryZoneDaoManager implements ITerritoryZoneDaoManager {
/*
#Autowired
private EntityManagerFactory emf;
*/
/*
* #PersistenceUnit EntityManagerFactory emf;
*
* #PersistenceContext private EntityManager getEntityManager(){ return
* emf.createEntityManager(); }
*/
#PersistenceContext
private EntityManager em;
private EntityManager getEntityManager() {
// return emf.createEntityManager();
return em;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#addTerritory(com.company.territoryzonelookup.dao.TerritoryZone)
*/
#Override
public TerritoryZone addTerritory(TerritoryZone territoryZone) {
EntityManager em = getEntityManager();
em.persist(territoryZone);
return territoryZone;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getTerritory(int)
*/
#Override
public TerritoryZone getTerritory(int id) {
TerritoryZone obj = null;
Query query = getEntityManager().createNamedQuery("selectById");
query.setParameter("id", id);
obj = (TerritoryZone) query.getSingleResult();
return obj;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#updateTerritory(com.company.territoryzonelookup.dao.TerritoryZone)
*/
#Override
public TerritoryZone updateTerritory(TerritoryZone territoryZone){
getEntityManager().merge(territoryZone);
return territoryZone;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getActiveTerritoriesByStateZipLob(java.lang.String, java.lang.String, java.util.Date, java.lang.String)
*/
#Override
public List<TerritoryZone> getActiveTerritoriesByStateZipLob(String stateCd, String zipCode, Date effectiveDate, String lobCd){
List<TerritoryZone> territoryList;
Query query = getEntityManager().createNamedQuery("selectActiveByZipStateLob");
query.setParameter("zipCode", zipCode);
query.setParameter("state", stateCd);
query.setParameter("lob",lobCd);
query.setParameter("effectiveDate", effectiveDate);
territoryList = (List<TerritoryZone>) query.getResultList();
return territoryList;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#deleteAll()
*/
#Override
public void deleteAll(){
Query query = getEntityManager().createNativeQuery("Delete from TerritoryZone");
query.executeUpdate();
}
/***
* the load method will remove all existing records from the database and then will reload it using it the data passed.
* #param terrList
*/
public void load(List<TerritoryZone> terrList){
deleteAll();
for (TerritoryZone terr:terrList){
addTerritory(terr);
}
}
}
Thanks in advance for your assistance.
Jeremy
jwmajors81
i can not know what's wrong with your unit testing code for lacking some details.
i also used spring unit test and dbunit for my himvc framework, a RAD framework based on spring3 and hibernate. here is the code of my super class for unit testingļ¼Œ
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:config/application-test-config.xml"})
#Transactional
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class HiMVCTransactionalUnitTest extends AbstractTransactionalJUnit4SpringContextTests{
#Autowired
protected DBUnitHelper dbHelper;
protected void loadFixture(){
try{
String fixtureFile=this.dbHelper.getDataFile();
if(fixtureFile==null){
fixtureFile=this.getDefaultXMLFixtureFile();
this.dbHelper.setDataFile(fixtureFile);
}
if(this.dbHelper.isDataFileExisted()){
if(this.dbHelper.isMSSQL()){
HiMVCInsertIdentityOperation operation=new HiMVCInsertIdentityOperation(DatabaseOperation.CLEAN_INSERT);
operation.setInTransaction(true);
this.dbHelper.executeDBOperation(operation);
}else{
this.dbHelper.executeDBOperation(DatabaseOperation.CLEAN_INSERT);
}
}
}catch(Exception x){
x.printStackTrace();
}
}
...
}
i use the #Transactional annotation in the class declaration, and also specified the transactionManager. i wrote a DBUnitHelper to wrap the dbunit details of data loading.
here is a unit test sample:
public class MyTest extends HiMVCTransactionalUnitTest{
#Before
public void setup(){
super.loadFixture();
}
//other testing methods
}
Hope these codes helpful.

Resources