I have this repository interface:
public interface ScoreCardRepository extends CrudRepository<ScoreCard, Long> {
#Query(value = "SELECT SUM(SCORE) FROM SCORE_CARD WHERE USER_ID = :userId", nativeQuery = true)
Integer getTotalScoreForUser(Long userId);
}
and this controller:
#RestController
#RequestMapping("/gamification")
public class GamificationController {
private final LeaderBoardServiceImpl leaderBoardService;
private final GameServiceImpl gameService;
#Autowired
public GamificationController(GameServiceImpl gameService, LeaderBoardServiceImpl leaderBoardService){
this.gameService = gameService;
this.leaderBoardService = leaderBoardService;
}
#GetMapping("/retrieve-stats")
ResponseEntity<GameStats> getUserStats(#RequestParam("user") String userId){
return ResponseEntity.ok(gameService.retrieveStatsForUser(Long.parseLong(userId)));
}
}
Now, when I call the /retrieve-stats and we go inside gameService.retrieveStatsForUser I get a null pointer exception
#Service
public class GameServiceImpl implements GameService {
private final ScoreCardRepository scoreCardRepository;
private final BadgeCardRepository badgeCardRepository;
#Autowired
public GameServiceImpl(ScoreCardRepository scoreCardRepository, BadgeCardRepository badgeCardRepository) {
this.scoreCardRepository = scoreCardRepository;
this.badgeCardRepository = badgeCardRepository;
}
#Override
public GameStats retrieveStatsForUser(Long userId) {
List<BadgeCard> badgeCardList = badgeCardRepository.findByUserIdOrderByBadgeTimestampDesc(userId);
--->>> int totalScore = scoreCardRepository.getTotalScoreForUser(userId); //NULL POINTER EXCEPTION
GameStats gameStats = new GameStats(userId, totalScore,
badgeCardList.stream().map(BadgeCard::getBadge).collect(Collectors.toList()));
return gameStats;
}
}
Does this mean that the scoreCardRepository bean doesn't get instantiated? That should happen in the #Autowired GamificationserviceImpl constructor right? the badgeCardRepository gets instantiated fine. What's happening?
I suggest another cause:
Integer getTotalScoreForUser(Long userId);
This method can return in Integer of null which causes a NPE during auto-boxing to primitive datatype int at
int totalScore = scoreCardRepository.getTotalScoreForUser(userId);
Related
Place that is complaining the error:
#Data
public class AluguelValorForm {
#Autowired
private ValorAluguelMultaService valorAluguelMultaService;
#NotNull #NotEmpty
private String idAluguel;
#NotNull
private Double valor;
public AluguelValor converter(AluguelValorRepository aluguelValorRepository, AluguelForm form ) {
Double valorAluguel = valorAluguelMultaService.valorAluguel(form);
return new AluguelValor(idAluguel,valorAluguel);
}
public AluguelValor update(String idAluguel,Double valor) {
AluguelValor aluguelValor = new AluguelValor();
aluguelValor.setId(idAluguel);
aluguelValor.setValor(valor);
return aluguelValor;
}
Repository:
#Repository
public interface AluguelValorRepository extends MongoRepository<AluguelValor, String> {
Aluguel getReferenceById(String id);
}
Place that I call the method in AluguelValorForm:
#PostMapping
//#CacheEvict(value = "listaDeTopicos",allEntries = true)
public void cadastrar(#RequestBody AluguelForm form) {
Optional<Carro> carro = carroRepository.findByPlaca(form.getPlaca_carro());
Optional<Cliente> cliente = clienteRepository.findByCpf(form.getCpf());
if(carro.isPresent() && cliente.isPresent()) {
Aluguel aluguel2 = form.converter(aluguelRepository);
aluguelRepository.save(aluguel2);
Double valorAluguel = valorAluguelMultaService.valorAluguel(form);
AluguelValor aluguelValor = aluguelValorForm.update(aluguel2.getId(), valorAluguel);
aluguelValorRepository.save(aluguelValor);
}
}
Problem solved. Apparently, it's not possible to #Autowired a class that doesn't have any bean, like my RentValue. That's why I got this error.
I've been writting some tests for a legacy web app on java using DeltaSpike, Weld, Hibernate and others frameworks.
I have faced some problem with my first test class that injects many Mocked interfaces on a Service.
Let me show some code:
Service to be tested
public class DistribuicaoProcessoManualServiceImpl extends DistribuicaoService implements DistribuicaoProcessoManualService {
#Inject
#WebServiceTJDBEntityManager EntityManager wstjEM;
private ForoTJComarcaMPRepository foroMPRepository;
private AssuntoTJNaturezaMPRepository naturezaMPRepository;
private VaraTJVaraMPRepository varaMPRepository;
private ClasseTJTipoMPRepository tipoMPRepository;
private ProcessoRepository processoRepository;
private UsuarioRepository usuarioRepository;
private UsuarioDaSessao usuarioDaSessao;
/**
* #deprecated CDI eyes only
*/
public DistribuicaoProcessoManualServiceImpl() {
this(null, null, null, null, null, null, null);
}
#Inject
public DistribuicaoProcessoManualServiceImpl(
ForoTJComarcaMPRepository foroMPRepository,
AssuntoTJNaturezaMPRepository naturezaMPRepository,
ClasseTJTipoMPRepository tipoMPRepository,
ProcessoRepository processoRepository,
UsuarioRepository usuarioRepository,
UsuarioDaSessao usuarioDaSessao,
VaraTJVaraMPRepository varaMPRepository) {
this.foroMPRepository = foroMPRepository;
this.naturezaMPRepository = naturezaMPRepository;
this.tipoMPRepository = tipoMPRepository;
this.processoRepository = processoRepository;
this.usuarioRepository = usuarioRepository;
this.usuarioDaSessao = usuarioDaSessao;
this.varaMPRepository = varaMPRepository;
}
#Override
#Transactional(readOnly = false, qualifier = {WebServiceTJDBEntityManager.class})
public Processo vinculaOuPreparaParaEspecializacao(Processo processo) {
//Service do some validations here with Guava's Preconditions;
Usuario usuario = usuarioRepository.procurarPorLogin(usuarioDaSessao.getUsuario().getLogin());
processo.atualizaInformacoesDeProcuradoriaEUsuario(usuario);
ProcessoDoTJMapeamentoParaMP processoDePara = compoeMapeamento(processo);
VaraTJVaraMP varaMP = varaMPRepository.procurarPorIdVaraTJForoEPorProcuradoria(processoDePara);
ForoTJComarcaMP comarcaMP = foroMPRepository.procurarPorIdForoEPorProcuradoria(processoDePara);
AssuntoTJNaturezaMP naturezaMP = naturezaMPRepository.procurarPorAssuntoEPorProcuradoria(processoDePara);
ClasseTJTipoMP tipoMP = tipoMPRepository.procurarPorCodigoEPorProcuradoria(processoDePara);
processoDePara
.comComarcaMP(comarcaMP)
.comVaraMP(varaMP)
.comNaturezaMP(naturezaMP)
.comTipoMP(tipoMP);
processoRepository.inclui(processo);
return processo;
}
}
The extended Class:
public abstract class DistribuicaoService {
public ProcessoDoTJMapeamentoParaMP compoeMapeamento(Processo processo) {
return new ProcessoDoTJMapeamentoParaMP(processo);
}
}
The mapper/builder/aggregator:
public class ProcessoDoTJMapeamentoParaMP implements Serializable {
private static final long serialVersionUID = 1L;
private final Processo processo;
public ProcessoDoTJMapeamentoParaMP(final Processo processo) {
this.processo = processo;
this.comCamaraMP( OrgaoJulgadorTJCamaraMP.semEfeito() )
.comRelatorMP( RelatorTJRelatorMP.semEfeito() )
.comOrigemMP( new OrgaoJulgadorTJOrigemMP() )
.comTipoDeRemessaMP();
}
public ProcessoDoTJMapeamentoParaMP comComarcaMP(ComarcaMP comarcaMP) {
processo.getDadosOrigem().setCodigoComarcaMP(comarcaMP.getIdComarca());
return this;
}
public ProcessoDoTJMapeamentoParaMP comVaraMP(VaraMP varaMP) {
processo.getDadosOrigem().setCodigoVaraMP(varaMP.getIdVaraMP());
return this;
}
public ProcessoDoTJMapeamentoParaMP comNaturezaMP(AssuntoTJNaturezaMP naturezaMP) {
processo.getAssuntoPrincipal().setCodigoNaturezaMP(naturezaMP.getIdNatureza());
return this;
}
public ProcessoDoTJMapeamentoParaMP comTipoMP(ClasseTJTipoMP tipoMP) {
processo.getDadosProcesso().getClasse().setCodigoTipoMP(tipoMP.getIdTipo());
return this;
}
ProcessoDoTJMapeamentoParaMP comCamaraMP(OrgaoJulgadorTJCamaraMP camaraMP) {
processo.getDadosDistribuicao().getMapeamento().setCodigoCamaraMP(camaraMP.getIdCamara());
return this;
}
ProcessoDoTJMapeamentoParaMP comRelatorMP(RelatorTJRelatorMP relatorMP) {
processo.getDadosDistribuicao().getMapeamento().setCodigoRelatorMP(relatorMP.getIdRelatorMP());
return this;
}
ProcessoDoTJMapeamentoParaMP comOrigemMP(OrgaoJulgadorTJOrigemMP origemMP) {
processo.getDadosDistribuicao().getMapeamento().setCodigoOrigemMP(origemMP.getIdOrigem());
return this;
}
ProcessoDoTJMapeamentoParaMP comTipoDeRemessaMP() {
processo.setTipoRemessa(TipoDeRemessa.fromTipoDeProcesso(this.processo.getProcessoVirtualTJ()));
return this;
}
public Procuradoria getProcuradoria() {
return processo.getProcuradoria();
}
public Integer getCodigoForoTJ() {
return processo.getDadosOrigem().getCodigoForoPrimeiraInstancia();
}
public Integer getCodigoVaraTJ() {
return processo.getDadosOrigem().getCodigoVaraPrimeiraInstancia();
}
public List<Parte> getPartes() {
return processo.getPartes();
}
public String getNomeAssuntoTJ() {
return processo.getAssuntoPrincipal().getDescricaoAssunto();
}
public Integer getCodigoClasseTJ() {
return processo.getDadosProcesso().getClasse().getCodigoClasse();
}
public String getNomeOrgaoJulgadorTJ() {
return processo.getDadosDistribuicao().getOrgaoJulgador().getNomeOrgaoJulgador();
}
public String getNomeRelatorTJ() {
return processo.getDadosDistribuicao().getRelator().getNomeRelator();
}
public Integer getIdAssuntoTJ() {
return processo.getAssuntoPrincipal().getCodigoAssunto();
}
public Movimentacao getUltimoMovimento() {
return null;
}
Class for Test:
#SuppressWarnings("deprecation")
#RunWith(MockitoJUnitRunner.class)
public class DistribuicaoProcessoManualServiceTest {
#Mock private ForoTJComarcaMPRepository foroMPRepository;
#Mock private AssuntoTJNaturezaMPRepository naturezaMPRepository;
#Mock private VaraTJVaraMPRepository varaMPRepository;
#Mock private ClasseTJTipoMPRepository tipoMPRepository;
#Mock private ProcessoRepository processoRepository;
#Mock private UsuarioRepository usuarioRepository;
#Mock private Usuario usuario;
#Mock private Procuradoria procuradoria;
#Mock private VaraTJVaraMP varaMP;
#Mock private ForoTJComarcaMP comarcaMP;
#Mock private AssuntoTJNaturezaMP naturezaMP;
#Mock private ClasseTJTipoMP tipoMP;
#Mock private UsuarioDaSessao usuarioDaSessao;
#InjectMocks
private DistribuicaoProcessoManualServiceImpl service = new DistribuicaoProcessoManualServiceImpl();
private Processo processo;
#Before
public void setUpMethod() {
initMocks(this);
//Others Builders for populate "processo" here;
processo = ProcessoBuilder()
.com(hoje)
.com(PARECER)
.com(FISICO)
.com(dadosProcesso)
.com(dadosOrigem)
.com(distribuicao)
.com(fila)
.cria();
ProcessoDoTJMapeamentoParaMP mapeamento = new ProcessoDoTJMapeamentoParaMP(processo);
when(usuario.getLogin()).thenReturn("luizrodriguesj");
when(usuario.getProcuradoria()).thenReturn(procuradoria);
when(usuarioDaSessao.getUsuario()).thenReturn(usuario);
when(usuarioRepository.procurarPorLogin(anyString())).thenReturn(usuario);
when(comarcaMP.getIdComarca()).thenReturn(1);
when(foroMPRepository.procurarPorIdForoEPorProcuradoria(mapeamento)).thenReturn(comarcaMP);
when(varaMP.getIdVaraMP()).thenReturn(1);
when(varaMPRepository.procurarPorIdVaraTJForoEPorProcuradoria(mapeamento)).thenReturn(varaMP);
when(naturezaMP.getIdNatureza()).thenReturn(1);
when(naturezaMPRepository.procurarPorAssuntoEPorProcuradoria(mapeamento)).thenReturn(naturezaMP);
when(tipoMP.getIdTipo()).thenReturn(1);
when(tipoMPRepository.procurarPorCodigoEPorProcuradoria(mapeamento)).thenReturn(tipoMP);
}
Method with problem:
#Test(expected = ApplicationException.class)
public void naoPermitirDistribuicaoDeProcessoSemUmMapeamentoDeVaraCorrespondente() {
service.vinculaOuPreparaParaEspecializacao(processo);
}
The interface that it was possible to mock:
public interface UsuarioRepository extends GenericDAO<Usuario, Long> {
boolean isAutenticavel(Usuario usuario);
Usuario procurarPorLogin(String login);
List<Usuario> lista(UsuarioPesquisa pesquisa, Paginacao paginacao);
}
One of Interfaces that not obey instruction mock and return null:
public interface ForoTJComarcaMPRepository extends GenericDAO<ForoTJComarcaMP, Long> {
ForoTJComarcaMP procurarPorIdForoEPorProcuradoria(ProcessoDoTJMapeamentoParaMP processoDePara);
List<ForoTJComarcaMP> lista(DominioMPPesquisa pesquisa, Paginacao paginacao);
ForoTJComarcaMP procuraPorForoTJ(ForoTJComarcaMP foroTJComarcaMP);
ForoTJComarcaMP procuraPorEntidade(ForoTJComarcaMP clone);
List<ForoTJComarcaMP> listaMapeamentoDistinctPor(Procuradoria procuradoria);
}
After others methods in this class that runs without any problem, this last one just fails because a NullPointerException (NPE) that occurs in:
VaraTJVaraMP varaMP = varaMPRepository.procurarPorIdVaraTJForoEPorProcuradoria(processoDePara);
AssuntoTJNaturezaMP naturezaMP = naturezaMPRepository.procurarPorAssuntoEPorProcuradoria(processoDePara);
ClasseTJTipoMP tipoMP = tipoMPRepository.procurarPorCodigoEPorProcuradoria(processoDePara);
ForoTJComarcaMP comarcaMP = foroMPRepository.procurarPorIdForoEPorProcuradoria(processoDePara);
....
// The references "comarcaMP", "varaMP", "naturezaMP", "tipoMP" are null here and next step throws a NPE.
processoDePara
.comComarcaMP(comarcaMP)
Other Mocked Interface declared work without problem, like this one:
Usuario usuario = usuarioRepository.procurarPorLogin(usuarioDaSessao.getUsuario().getLogin());
Where the "usuario" reference is setted as declared on setup method in Test Class.
Sorry for any mistake or lack of more informations.
Thanks and best regards!
I have class
public class CloneUserService {
private final UserRepository userRepository;
private final PersonRepository personRepository;
private final OrderRepository orderRepository;
public CloneUserService(UserRepository userRepository, PersonRepository personRepository, OrderRepository orderRepository) {
this.userRepository = userRepository;
this.personRepository = personRepository;
this.orderRepository = orderRepository;
}
public void createFromTemplate(String templateUserId) {
User templateUser = userRepository.getUserById(templateUserId);
Person templatePerson = personRepository.getPersonByUserId(templateUserId);
List<Order> templateOrders = orderRepository.getOrdersByUserId(templateUserId);
User newUser = cloneUserFromTemplate(templateUser);
newUser.setId("newId");
userRepository.save(newUser);
Person newPerson = clonePersonFromTemplate(templatePerson);
newPerson.setUser(newUser);
newPerson.setId("newId");
personRepository.save(newPerson);
for (Order templateOrder : templateOrders) {
Order newOrder = cloneOrderFromTemplate(templateOrder);
newOrder.setId("newId");
newOrder.setUSer(newUser);
orderRepository.save(newOrder);
}
}
private Order cloneOrderFromTemplate(Order templateOrder) {
//logic
return null;
}
private Person clonePersonFromTemplate(Person templatePerson) {
//logic
return null;
}
private User cloneUserFromTemplate(User templateUser) {
//logic
return null;
}
}
I need to test this method createFromTemplate.
I create this test. I create stabs for each repository and store saved object into this stub. And I add the additional method for getting this object for the assertion.
It works. But I have 2 problems:
My template object is mutable. It is not a big problem but it is a fact.
If I add new methods to repository interface I must implement it in stubs.
Mu question - How can I test cloned objects like theses from my example?
I don't use spring and H2DB or another in-memory database.
I have a MongoDB database.
If I use mockito I will not understand how to assert new objects in void method.
class CloneUserServiceTest {
private CloneUserService cloneUserService;
private UserRepositoryStub userRepository;
private PersonRepositoryStub personRepository;
private OrderRepositoryStub orderRepository;
#Before
public void setUp() {
User templateUser = new User();
Person templatePerson = new Person();
List<Order> templateOrders = Collections.singletonList(new Order());
userRepository = new UserRepositoryStub(templateUser);
personRepository = new PersonRepositoryStub(templatePerson);
orderRepository = new OrderRepositoryStub(templateOrders);
cloneUserService = new CloneUserService(userRepository, personRepository, orderRepository);
}
#Test
void createFromTemplate() {
cloneUserService.createFromTemplate("templateUserId");
User newUser = userRepository.getNewUser();
// assert newUser
Person newPerson = personRepository.getNewPerson();
// assert newPerson
Order newOrder = orderRepository.getNewOrder();
// assert newOrder
}
private static class UserRepositoryStub implements UserRepository {
private User templateUser;
private User newUser;
public UserRepositoryStub(User templateUser) {
this.templateUser = templateUser;
}
public User getUserById(String templateUserId) {
return templateUser;
}
public void save(User newUser) {
this.newUser = newUser;
}
public User getNewUser() {
return newUser;
}
}
private static class PersonRepositoryStub implements PersonRepository {
private Person templatePerson;
private Person newPerson;
public PersonRepositoryStub(Person templatePerson) {
this.templatePerson = templatePerson;
}
public Person getPersonByUserId(String templateUserId) {
return templatePerson;
}
public void save(Person newPerson) {
this.newPerson = newPerson;
}
public Person getNewPerson() {
return newPerson;
}
}
private static class OrderRepositoryStub implements OrderRepository {
private List<Order> templateOrders;
private Order newOrder;
public OrderRepositoryStub(List<Order> templateOrders) {
this.templateOrders = templateOrders;
}
public List<Order> getOrdersByUserId(String templateUserId) {
return templateOrders;
}
public void save(Order newOrder) {
this.newOrder = newOrder;
}
public Order getNewOrder() {
return newOrder;
}
}
}
In your scenario I would consider using mocking framework like Mockito.
Some main advantages:
Adding new methods to repository interface doesn't require implementing it in stubs
Supports exact-number-of-times and at-least-once verification
Allows flexible verification in order (e.g: verify in order what you want, not every single interaction)
Very nice and simple annotation syntax - #Mock, #InjectMocks, #Spy
Here is an example - maybe it will interest you:
// arrange
Warehouse mock = Mockito.mock(Warehouse.class);
//act
Order order = new Order(TALISKER, 50);
order.fill(warehouse); // fill will call remove() implicitly
// assert
Mockito.verify(warehouse).remove(TALISKER, 50); // verify that remove() method was actually called
In my Spring Boot project I have defined a following RestController method:
#PreAuthorize("hasAuthority('" + Permission.APPEND_DECISION + "')")
#RequestMapping(value = "/{decisionId}/decisions", method = RequestMethod.PUT)
public DecisionResponse appendDecisionToParent(#PathVariable #NotNull #DecimalMin("0") Long decisionId, #Valid #RequestBody AppendDecisionRequest decisionRequest) {
....
return new DecisionResponse(decision);
}
Right now in order to provide allowed authority name I use a following code construction:
#PreAuthorize("hasAuthority('" + Permission.APPEND_DECISION + "')")
where Permission.APPEND_DECISION is a constant:
public static final String APPEND_DECISION = "APPEND_DECISION";
Is there any more elegant way in Java/Spring in order to define such kind of code ?
Here is a simple approach to defining authorities in a single place that doesn't require any in-depth Spring Security config.
public class Authority {
public class Plan{
public static final String MANAGE = "hasAuthority('PLAN_MANAGE')";
public static final String APPROVE = "hasAuthority('PLAN_APPROVE')";
public static final String VIEW = "hasAuthority('PLAN_VIEW')";
}
}
Securing services...
public interface PlanApprovalService {
#PreAuthorize(Authority.Plan.APPROVE)
ApprovalInfo approvePlan(Long planId);
}
}
Thanks to oli37 I have implemented this logic in a following way:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodExpressionHandler;
}
public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport(Permission.class.getPackage().getName());
return standardEvaluationContext;
}
}
}
#PreAuthorize("hasAuthority(T(Permission).APPEND_DECISION)")
#RequestMapping(value = "/{decisionId}/decisions", method = RequestMethod.PUT)
public DecisionResponse appendDecisionToParent(#PathVariable #NotNull #DecimalMin("0") Long decisionId, #Valid #RequestBody AppendDecisionRequest decisionRequest) {
...
return new DecisionResponse(decision);
}
I thing the good way is not to mixed both
You can have constants
public static final String ROLE_ADMIN = "auth_app_admin";
and have that other side
#PreAuthorize("hasRole(\"" + Constants.ROLE_ADMIN + "\")")
this is much clear
I am trying to use the automatic versioning of Hibernate but when the update method f of the Session is called I do not see the version field in the where clause of the query nor is the version incremented in the database. I am doing something fundamentally wrong probably, but what? Is calling getCurrentSession of sesssionFactory an issue?
I have the following entity class:
package dslibweb.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;
#Entity
#Table(name = "dsXCT_Recalls")
public class DsXCT_Recalls {
#Id
public String recallId;
public int version;
public String status;
//...... more properties.....
#Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public String getRecallId() {
return recallId;
}
public void setRecallId(String recallId) {
this.recallId = recallId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
My controller:
package recalls.controller;
#Controller
public class RecallsDataController {
#Autowired
RecallsService recallsManager;
#Autowired
AuthenticationService authService;
private static final Logger logger = Logger.getLogger(RecallsDataController.class);
private static final String SAVE_RECALLS = "MODIFY XCT RECALLS";
RecallsGrid recalls;
#RequestMapping(value = "/showRecallsGrid")
#ResponseBody
public RecallsGrid showRecallsGrid( HttpSession session, HttpServletResponse response) {
recalls = recallsManager.getRecallsDataGrid((String) session.getAttribute("socketToken"), new GridFilters(0, 0, "", "", "", "", ""));
if (recalls.getError() == null || recalls.getError().equals("")) { // no error
return recalls;
} else {
try {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, recalls.getError());
} catch (IOException e) {
e.printStackTrace();
}
return recalls;
}
}
#RequestMapping(value = "/saveRecalls" , method= RequestMethod.POST)
#ResponseBody
public String saveRecalls( HttpSession session, #RequestParam(value="ids[]", required = false) String [] ids, #RequestParam(value="statuses[]", required = false) String [] statuses){
boolean result = authService.validateUserAction((String) session.getAttribute("socketToken"), SAVE_RECALLS);
if(result)
return recallsManager.saveRecalls(ids, statuses, recalls);
else
return "You do not have authority to perform this action.";
}
}
Where I retrieve a collection of DsXCT_Recalls and show them to the user. The collection is stored in the controller. The user then changes status in one or more entities and I call the saveRecalls method of the recallManager which creates a list of only the changed entities (comparing with the collection stored in the controller).
The recallsManager (service layer) is:
package recalls.service.defaultimpl;
#Service("recallManager")
public class HibernateRecallsDataService implements RecallsService {
#Autowired
JsonRpcRequest jsonReq;
#Autowired
JsonRpcSocketWriterReader socketWriterReader;
#Autowired
JsonRpcRequestConstructor reqConstructor;
#Autowired
RecallsDao hibernateRecallsDao;
private static final Logger logger = Logger.getLogger(HibernateRecallsDataService.class);
#Transactional
public RecallsGrid getRecallsDataGrid(String socketToken, GridFilters filters) {
List<DsXCT_Recalls> recalls = hibernateRecallsDao.findRangeOfRecordsFiltered(filters);
return new RecallsGrid(recalls);
}
#Transactional()
public String saveRecalls(String[] ids, String[] statuses, RecallsGrid recalls) {
List<DsXCT_Recalls> recallList = recalls.getRecalls();
List<DsXCT_Recalls> updatedRecallList = new ArrayList<DsXCT_Recalls>();
for (int i = 0; i < ids.length; i++) {
for (DsXCT_Recalls recall : recallList) {
if (recall.recallId.equals(ids[i])) { // recall is found in the list
if (!statuses[i].equals(recall.getStatus())) { // status has changed
recall.setStatus(statuses[i]);
updatedRecallList.add(recall);
}
}
}
}
return hibernateRecallsDao.saveAll(updatedRecallList);
}
}
The saveAll method of my DAO calls one update method of hibernate session by entity changed:
package recalls.dao.hibernate;
#Repository
public class HibernateRecallsDao implements RecallsDao {
#Autowired(required = true)
#Resource(name = "mySessionFactory")
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
public List<DsXCT_Recalls> findRangeOfRecordsFiltered(GridFilters filters) {
return sessionFactory.getCurrentSession().createQuery("from DsXCT_Recalls r WHERE SID = 0 ORDER BY Org, Bank, BIC, SetlDate").list();
}
public String saveAll(List<DsXCT_Recalls> recallList){
int count = 0;
for(DsXCT_Recalls recall:recallList){
sessionFactory.getCurrentSession().update(recall);
count++;
}
return count + " recalls were modified.";
}
}
So apparently the #Version must be above the attribute declaration and not above the getter method.. I am sure I saw this somewhere though. So much time wasted :(