in a RestController:
#RestController
#RequestMapping("/apks")
public class ApkController {
#Inject
DecompiledApkRepository decompiledApkRepository;
#Autowired
DecompileService decompileService;
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createFromJson(#RequestBody Apk apk) {
....
DecompiledApk decompiledApk = new DecompiledApk(apk, apkFile, apk.getMd5Hash());
decompileService.decompile(decompiledApk, apk.getMd5Hash(), decompiledApkRepository);
} catch (IOException |InterruptedException e) {
e.printStackTrace();
}
return new ResponseEntity<>(null, responseHeaders, HttpStatus.CREATED);
}
The DecompiledApk entity:
#Entity
#Table(name = "decompiled_apks")
public class DecompiledApk {
#Id
#GeneratedValue
#JsonIgnore
private Long id;
#MapsId
#OneToOne(optional=false)
private Apk apk;
#Column(unique = true)
private URI decompiledFolder;
#Transient
#JsonIgnore
private File inputApk;
// all public getters/setters, package empty constructor and public full constructor
The DecompiledApkRepository:
#Repository
public interface DecompiledApkRepository extends CrudRepository<DecompiledApk, Long> {
DecompiledApk findByApk_md5Hash(String md5Hash);
}
And here, the problem is in the async method in the DecompileService:
#Service
public class DecompileService {
private static final String DEC_FOLDER = "/tmp/decompiled/";
#Async
public Future<Void> decompile(DecompiledApk decompiledApk, String md5Hash, DecompiledApkRepository decompiledApkRepository) throws InterruptedException {
/*
...
*/
decompiledApk.setDecompiledFolder(URI.create(outputFolder));
System.err.println("---start-->"+Thread.currentThread().getName());
decompiledApkRepository.save(decompiledApk);
System.err.println("---end-->"+Thread.currentThread().getName());
return new AsyncResult<>(null);
}
The instruction:
decompiledApkRepository.save(decompiledApk);
throw:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.xxx.domain.Apk; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.xxx.domain.Apk
But if I remove the #Async annotation it works without problem!
Any ideas?
Do you need more details?
Although the method has been marked as asynchronous, there are no spring transaction markers , suspect that is causing is the problem.
Try adding Spring transaction :
org.springframework.transaction.annotation
#Transactional
Hopefully that will solve the issue.
Spring's transaction context is preserved using ThreadLocals. This means that your SessionFactory is only available to the thread dispatching your request thus, if you create a new thread, you will get a null and a corresponding exception.
What your #Async method does is use a TaskExecutor to run your method in another thread. So the problem described above is happening with your service.
According to link
Related
I observed that the .save() method executes an extra SELECT query to check whether the corresponding record already exists when the corresponding ID is not a AUTO INCREMENT one.
I tried to implement a repository for this kind of situation that will be extended by many JpaRepository interfaces which will be used across different stateless services and I would like to know if my code is safe - race conditions wise - accross multiple requests as I am not that comfortable using the EntityManager yet.
User Entity :
public class User {
#Id
#Column(name = "username", nullable = false, length = 45)
private String username;
#Column(name = "password", nullable = false, length = 256)
private String password;
}
Solution 1 :
public interface SimpleRepository<T> {
void persist(T entity);
}
public class SimpleRepositoryImpl<T> implements SimpleRepository<T> {
#PersistenceContext
EntityManager entityManager;
#Transactional
#Override
public void persist(T entity) {
entityManager.persist(entity);
}
}
User Repository :
public interface UserRepository extends JpaRepository<User, String>, SimpleRepository<User> {}
User Service :
#Service
#AllArgsConstructor
public class UserService {
private final UserRepository userRepository;
public void createUser(User user) {
this.userRepository.persist(user);
}
}
The same implementation will be followed across many different JPA Repositories and Services in the application.
If the solution above is not safe how about this one?
#Service
public class PersistenceService {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public <T> void persist(T entity) {
entityManager.persist(entity);
}
}
Which will turn my UserService and every other Service that is in need of the same functionality to :
#Service
#AllArgsConstructor
public class UserService {
private final PersistenceService persistenceService;
public void createUser(User user) {
this.persistenceService.persist(user);
}
}
Environment:
Jboss 7.2
Java 11
The CourseService(EJB) find the course with findById() method by Id an then try to lazy loading courseModuls with size() function, but I get LazyInizialitzationException.
This is a migration from Jboss 5.2 and it worked properly but with Jboss7.2 I get this error.
Any idea what is that?
CourseService
#Stateless
#Local
#RolesAllowed(RolConstants.EVERYONE)
public class CourseService extends BusinessService<Course> implements CourseServiceable {
...
#Override
#PermitAll
public Course findByIdPartial(Object id) throws AppException {
Course Course = findById(id);
Course.getCourseModuls().size();
return Course;
}
...
CourseBean
#Named
#ViewScoped
public class CourseBean extends LazyBean<Course> implements Serializable {
...
public void load() {
try {
selected = service.findByIdPartial(selected.getId());
} catch (AppException e) {
log.error("CourseBean.load()", e);
faceMessage.error("error.load", e);
}
}
...
Error
12:15:19,160 SEVERE [org.primefaces.application.exceptionhandler.PrimeExceptionHandler] (default task-1) failed to lazily initialize a collection of role: es.caib.forma.business.form.entity.Course.courseModuls, could not initialize proxy - no Session: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: es.caib.forma.business.form.entity.Course.courseModuls, could not initialize proxy - no Session
at org.hibernate#5.3.7.Final-redhat-00001//org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:597)
at org.hibernate#5.3.7.Final-redhat-00001//org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:216)
at org.hibernate#5.3.7.Final-redhat-00001//org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:160)
at org.hibernate#5.3.7.Final-redhat-00001//org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:287)
at deployment.fora2.ear//es.caib.fora.business.formacio.boundary.CourseService.findByIdPartial(CourseService.java:337)
at deployment.form2.ear.forma-front.war//es.caib.forma.presentation.front.form.CourseBean.load(CourseBean.java:104)
Course
#Entity
#Table(name = "COURSE")
public class Course implements Serializable {
...
#OneToMany(mappedBy = "curs", cascade = { CascadeType.MERGE, CascadeType.REMOVE, CascadeType.REFRESH })
private List<CursModul> cursModuls;
...
You should add annotation #Transactional to the method findByIdPartials(), because when method findById() return Course then transaction end too.
There you can find more information about this annotation: link
Correct code below:
#Override
#PermitAll
#Transactional
public Course findByIdPartial(Object id) throws AppException {
Course Course = findById(id);
Course.getCourseModuls().size();
return Course;
}
I have a #SessionScope bean that keeps track of the current users role. When I run the application the value is present, however when I run my integration tests the bean is null.
Here's what I have:
#Component
#SessionScope
public UserSessionDataImpl implements UserSessionData {
private String role; // "Admin" or "User"
// getters/setters below
}
// Service
#Service("roleService")
public RoleServiceImpl implements RoleService {
#Autowired
UserSessionData sessionData;
public String getRole(){
return this.sessionData.getRole();
}
public String setRole(String role){
return this.sessionData.setRole(role);
}
}
// API
#Api
public class TicketApi {
#Autowired
private RoleService roleService;
#Autowired
private TicketService TicketService;
#RequestMapping(value = "person/{id}/tickets", method = RequestMethod.GET)
public String getTickets(long personId) {
// only admins can lookup tickets
if(roleService.getRoles.equals("Admin"){
// do logic
}
}
}
// Unit test method
#Before
public void setup(){
roleService.setRole("Admin"); //set role to admin for testing
}
#Test
// Calls TicketApi
public void getTicketsTest(){
mockMvc.perform(
get("/person/{id}/tickets")); // blows up due to null role
}
I am stumped as to why my roleSerivce loses the reference to sessionData. I do see that UserSessionDataImpl does get instantiated multiple times, which I wouldn't think would happen. I'm wondering if the mockMvc call creates a new Session which would cause the extra instantiations. Has anyone else figured this issue out?
I have created a repository but when I call my repository it gives a NullPointerException everytime. Can someone help me figure out why?
My repository
#Repository
public interface WorkflowObjectRepository extends CrudRepository<WorkflowObject, String> {
#Override
WorkflowObject findOne(String id);
#Override
void delete(WorkflowObject workflowObject);
#Override
void delete(String id);
#Override
WorkflowObject save(WorkflowObject workflowObject);
}
My Object
#Data
#Entity
#Table(name = "workflowobject")
public class WorkflowObject {
#GeneratedValue
#Column(name="id")
private String id;
#Column(name = "state_name")
private String stateName;
}
My test
public class Test {
#Autowired
static WorkflowObjectRepository subject;
public static void main(String[] args) {
final WorkflowObject obj = new WorkflowObject();
obj.setId("maha");
obj.setStateName("test");
subject.findOne("maha");
}
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/vtr?
autoReconnect=true
spring.datasource.username=vtr
spring.datasource.password=vtr
The problem is you are trying to autowire a static data member
#Autowired
static WorkflowObjectRepository subject;
What happens in your case is static is getting initialized before the bean so you are autowiring on null, just remove the static and deal with it as instance variable.
repositories are singletones so no point of making them static
In order to work properly with #Autowired you need to keep in mind that spring scans annotations to allow automatically classes load.
If you need to #Autowired some class, the class Test needs to have some annotation, that tells to Spring Boot to find it.
Your Test class need to have #Component, #Service, #Controller, #Repository, etc. Otherwise #Autowired will not work because Spring Boot not know your class and will not process it.
You can find some explanation here
In my webapp, Spring transaction and Hibernate session API are used.
Please see my service and DAO classes and usage below;
BizCustomerService
#Service
#Transactional(propagation = Propagation.REQUIRED)
public class BizCustomerService {
#Autowired
CustomerService customerService;
public void createCustomer(Customer cus) {
//some business logic codes here
customerService.createCustomer(cus);
//***the problem is here, changing the state of 'cus' object
//it is necessary code according to business logic
if (<some-check-meet>)
cus.setWebAccount(new WebAccount("something", "something"));
}
}
CustomerService
#Service
#Transactional(propagation = Propagation.REQUIRED)
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CustomerService {
#Autowired
CustomerDAO customerDao;
public Long createCustomer(Customer cus) {
//some code goes here
customerDao.save();
}
}
CustomerDAO
#Repository
public class CustomerDAO {
#Autowired
private SessionFactory sessionFactory;
private Session getSession() {
return sessionFactory.getCurrentSession();
}
public Long save(Customer customer) {
//* the old code
//return (Long) getSession().save(customer);
//[START] new code to change
Long id = (Long) getSession().save(customer);
//1. here using 'customer' object need to do other DB insert/update table functions
//2. if those operation are failed or success, as usual, they are under a transaction boundary
//3. lets say example private method
doSomeInsertUpdate(customer);
//[END] new code to change
return id;
}
//do other insert/update operations
private void doSomeInsertUpdate(customer) {
//when check webAccount, it is NULL
if (customer.getWebAccount() != null) {
//to do something
}
}
}
Customer
#Entity
#Table(name = "CUSTOMER")
public class Customer {
//other relationships and fields
#OneToOne(fetch = FetchType.LAZY, mappedBy = "customer")
#Cascade({CascadeType.ALL})
public WebAccount getWebAccount() {
return this.webAccount;
}
}
In the above code, customer is created in BizCustomerService then may change the state of related WebAccount after persisted through DAO. And when the transaction is committed, a new Customer and related WebAccount object are persisted to DB. This is normal I know.
The problem is; in CustomerDAO#save() >> doSomeInsertUpdate() method, 'webAccount' is NULL and the value is not set yet at that time.
EDIT : Left to mention that, it is restricted and don't want to change the code at BizCustomerService and CustomerService because there can be many invocations to DAO methods it can impact alot. So want to change only at DAO level.
So my question is how can I access WebAccount object in doSomeInsertUpdate() method? Any Hibernate usage needed?
Thanks in advance!!
not sure what you're expecting, but I think there is no magic here. If you want a WebAccount to be != null, you'll have to explicitly make one and save it to the DB.
WebAccount wb = new WebAccount();
getSession().save(wb);
customer.setWebAccount(wb);