I have a weird issue with #Transactional, the thing is, it's not doing a complete rollback on an error and I don't get why.
This is my service:
#Transactional
public class AService {
#Autowired
private AuditRepository auditRepository;
#Autowired
private RequestStatusRepository requestStatusRepository;
#Autowired
private ApprovementRepository approvementRepository;
public void approve(long approvementId) {
updateStatus();
}
private void updateStatus(long approvementId){
Approvement approvement = approvementRepository.findById(approvementId);
updateApprovement(approvement);
Request request = requestRepository.findById(approvement.getRequest().getId());
updateRequest(request);
}
private void updateApprovement(){
approvementRepository.save(approvement);
}
private void updateRequest(Request request){
requestRepository.save(request);
auditRepository.save(new Audit(request));
}
}
This is the Approvement Repository they are all similar:
#Repository
public class ApprovementRepositoryImpl implements ApprovementRepository {
#Autowired
private JpaApprovementRepository jpaApprovementRepository;
private Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
#Override
public Approvement findById(long id) {
return jpaApprovementRepository.findById(id).map(this::to).orElse(null);
}
#Override
public void save(Approvement approvement) {
jpaApprovementRepository.save(from(approvement));
}
public Approvement to(ApprovementEntity from){
return mapper.map(from, Approvement.class);
}
public ApprovementEntity from(Approvement to){
return mapper.map(from, ApprovementEntity.class);
}
}
Ok, the request I'm getting has a very long field and the auditRepository isn't being able to store it, the change in audit never happened, and the change in request gets rolled back, but, the change in approvement it's being commited.
Why is this happening? I've been trying different types of propagations, and moving the #Transactional annotation from the class to the public method without success, any ideas would be great, thanks.
Related
i have a #Service that is composed:
#Service
public class TestService implements ITestService {
#Autowired
private TestRepository testRepository;
#Autowired
private TransactionTableRepository transactionRepository;
#Transactional
#Override
public void post(String data) throws Exception {
testRepository.post(data);
//here i have finish my process and i want to save the current
//transaction id in a db oracle table
transactionRepository.save(getCurrentTransactionId());
}
}
How can i have the getCurrentTransactionId()??
Thanks for the help
So I have to make a CRUD application and when run on postman, the error being shown is 405, Method not allowed. Upon trying "spring.mvc.hiddenmethod.filter.enabled: true" in the application.properties file, the code still showed the same error when tested using postman.
This is the controller
#RestController
#RequestMapping("/employees")
public class EmpController {
#Autowired
private EmpService empService;
#Autowired
private EmpRepository empRepo;
#GetMapping("")
public List<Employee> getAllEmployees()
{
return empService.getAllEmployees();
}
#PostMapping("")
public void addEmployee(#RequestBody Employee emp)
{
empService.addEmployee(emp);
}
#PutMapping("/{id}")
public void updateEmployee(#PathVariable String id,#RequestBody Employee emp)
{
empService.updateEmployee(id,emp);
}
#DeleteMapping(path="/{id}")
public void deleteEmployee(#PathVariable String id) {
System.out.println("Delete function");
empService.deleteEmployee(id);
}
}
This is the service
#org.springframework.stereotype.Service
public class EmpService {
#Autowired
public EmpRepository empRepo;
public List<Employee> getAllEmployees(){
List<Employee> employees = new ArrayList<>();
empRepo.findAll().forEach(employees::add);
return employees;
}
public void addEmployee(Employee emp) {
empRepo.save(emp);
}
public void updateEmployee(String id, Employee emp) {
empRepo.save(emp);
}
public void deleteEmployee(String id) {
empRepo.deleteById(id);
}
}
on trying the the other put post and get methods the code was working perfectly fine. But this is the only place where I found the 405 error.
Try this
#DeleteMapping(path="/{id}")
public void deleteEmployee(#PathVariable("id") String id) {
System.out.println("Delete function");
empService.deleteEmployee(id);
}
Could you put your request URL here? Maybe you used the wrong URL.
The right URL should be:
http://{host}:{port}/employees/1
1 is the id you want to delete.
I'm trying to improve a unit test for a service I'm developing. I read somewhere that it's ideal to use #WebMvcTest annotation over #SpringBootTest when testing the Web or Controller layer.
However, for some reason, a #MockBean service field I am using in the Test class is always NULL.
java.lang.AssertionError: Expecting actual not to be null
In the test class below, serviceIsLoaded() method is always null when I run that single test.
SectionRESTControllerTest.java
#WebMvcTest
public class SectionRESTControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SectionServiceImpl sectionServiceImpl;
#Test
public void serviceIsLoaded() {
assertThat(sectionServiceImpl).isNotNull();
}
}
SectionServiceImpl.java
#Service
public class SectionServiceImpl implements SectionService {
private final Logger logger = LoggerFactory.getLogger(SectionServiceImpl.class);
#Autowired
private SectionRepository sectionRepo; //saves to section table
#Autowired
private GradeLevelRepository gradeLevelRepo;
#Override
public Section createSection(Section section) {...}
#Override
public Section updateSection(Section request) throws Exception {...}
#Override
public Section getSectionById(Long id) {
return sectionRepo.findById(id).get();
}
#Override
public List<Section> getAllActiveSections() {
return sectionRepo.findSectionByIsActiveTrue();
}
#Override
public List<Section> getAllInActiveSections() {
return sectionRepo.findSectionByIsActiveFalse();
}
#Override
public List<Section> getSectionsByGradeLevelId(Long id) {
return sectionRepo.findByGradeLevelId(id);
}
#Override
public List<Section> getSectionByGradeLevelCode(String code) {
return sectionRepo.findByGradeLevelCode(code);
}
#Override
public List<Section> getSectionByGradeLevelCategory(String category) {
return sectionRepo.findByGradeLevelCategory(category);
}
}
My understanding is, with #WebMvcTest, it does not load the entire application context with all managed beans which makes the UnitTest run faster. Unlike, #SpringBootTest which loads everything when running the UnitTest.
SectionService.java
public interface SectionService {
Section createSection(Section section);
Section updateSection(Section section) throws Exception;
Section getSectionById(Long id);
List<Section> getAllActiveSections();
List<Section> getAllInActiveSections();
List<Section> getSectionsByGradeLevelId(Long id);
List<Section> getSectionByGradeLevelCode(String code);
List<Section> getSectionByGradeLevelCategory(String category);
}
Main class
#SpringBootApplication
public class AutoformSettingsServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AutoformSettingsServiceApplication.class, args);
}
}
Do you have any thoughts or ideas why the #MockBean sectionServiceImpl is null
Thank you.
I want to call all the request mapping method(which has #Resource injection) before the server starts. How I can do this?
#Controller
public class ServiceController {
#Resource(name="userService")
private IUserService userService;
#RequestMapping("/getAllCountry")
public String getAllCountry() {
return userService.getAllCountry();
}
#RequestMapping("/getAllStates")
public String getAllStates() {
return userService.getStates();
}
#PostConstruct
public void cacheData(){
cache.put("ALL_COUNTRY_DATA", getAllCountry());
cache.put("ALL_STATE_DATA", getAllStates());
}
}
The above code fails and give me IllegalStateException. What is the best way to call the request mapping methods before the server is up and populate the cache.
Try using ApplicationListener in conjunction with ContextRefreshedEvent:
#Controller
public class ServiceController implements ApplicationListener<ContextRefreshedEvent> {
private static final Map<String, String> cache = new HashMap<>();
#Resource(name = "userService")
private IUserService userService;
#RequestMapping("/getAllCountry")
public String getAllCountry() {
return userService.getAllCountry();
}
#RequestMapping("/getAllStates")
public String getAllStates() {
return userService.getStates();
}
public void cacheData() {
cache.put("ALL_COUNTRY_DATA", getAllCountry());
cache.put("ALL_STATE_DATA", getAllStates());
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
cacheData();
}
}
I have following Spring Service
#Service
class FeatureTogglesImpl implements FeatureToggles {
private final FeatureToggleRepository featureToggleRepository;
private Map<String, Feature> featuresCache;
#Autowired
public FeatureTogglesImpl(final FeatureToggleRepository featureToggleRepository) {
this.featureToggleRepository = featureToggleRepository;
this.featuresCache = loadAllFromRepository();
}
#Override
#Transactional
public void enable(Feature feature) {
Feature cachedFeature = loadFromCache(feature);
cachedFeature.enable();
featureToggleRepository.save(cachedFeature);
onFeatureToggled();
}
#Override
public boolean isEnabled(Feature feature) {
return loadFromCache(feature).isEnabled();
}
private Feature loadFromCache(Feature feature) {
checkNotNull(feature);
return featuresCache.get(feature.getKey());
}
private Map<String, Feature> loadAllFromRepository() {
return Maps.uniqueIndex(featureToggleRepository.findAll(), new Function<Feature, String>() {
#Override
public String apply(Feature feature) {
return feature.getKey();
}
});
}
void onFeatureToggled() {
featuresCache = loadAllFromRepository();
}
}
As you can see,I store loaded features into featuresCache, so that when client calls isEnabled() it is loading according feature from the cache.
There is a managed bean, who manages toggling the feature,
#Component
#ManagedBean
#Scope("view")
public class FeatureTogglesManager {
#Autowired
private FeatureToggles featureToggles;
#Secured({"ROLE_FEATURE_TOGGLES_EDIT"})
public String enable(Feature feature) {
featureToggles.enable(feature);
return null;
}
}
When I call enable() from AdminFeatureTogglesManager , I can see proper feature toggled, and cache pre-populated.
I have another service, which actually uses FeatureToggles.isEnabled() service
#Service
class ProductServieImpl implements ProductService {
#Autowired
private FeatureToggles featureToggles;
#Override
#Transactional
public void loadProducts() {
if (featureToggles.isEnabled(NewProducts.insance())) {
loadNewProducts();
return;
}
loadOldProducts();
}
}
The problem is that featureToggles.isEnabled() from this service always returns old instance from the cache, and when I debug the FeatureTogglesImpl, I do not see my pre-populated cache, although after toggle I could see correct/updated cache.
Isn't FeatureTogglesImpl supposed to be a singletong, so that if I change instance variable, it changes everywhere? Any help is appreciated.