In my java spring mvc application, i am using the below code:
The repository:
#Repository
public interface TransactionRepository extends JpaRepository<Transactions ,Long >{
#Query(value = "SELECT sum( value) FROM Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ", nativeQuery=true)
public double getOverAllValue(String merchantID,String ageGroup);
}
and then i made a serivce which works based on this #Repository:
#Service
public class IncomeDataService {
#Autowired
TransactionRepository transactionRepo;
public ArrayList<Double> dataCollector(int merchantID){
ArrayList<Double> data= new ArrayList<Double>();
for(int i=0;i<10;i++)
data.add( transactionRepo.getOverAllValue(Integer.toString(merchantID),Integer.toString(i)));
return data;
}
and then i am trying to test the written service using the junit:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SEBApplication.class)
public class IncomeDataServiceTest {
#Autowired
IncomeDataService incomeDataService;
#Test
public void testDataCollector() {
System.out.println(incomeDataService.dataCollector(1));
}
}
But, when i run my test it complains with:
org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public abstract double ee.seb.domain.repository.TransactionRepository.getOverAllValue(java.lang.String,java.lang.String)
Update your Query's return type from primitive data type double to Double Because your query can return null value which will be accepted by wrapper class and not by primitive, so your final Query will be like this:
#Query(value = "SELECT sum( value) FROM Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ", nativeQuery=true)
public Double getOverAllValue(String merchantID,String ageGroup);
Related
I am trying to do testing my save method in my service impl class. It has Page as return type. The test succeeds but I am writting something wrong because it succeeds for all the cases which normally shouldn't Please see my code below.
Service Class Implementation
#Service
#Transactional
public class CompanyServiceImpl implements CompanyService {
private final CompanyRepository companyRepository;
public CompanyServiceImpl(CompanyRepository companyRepository) {
this.companyRepository = companyRepository;
}
#Override
public Page<Company> findAll(Pageable pageable) {
Page<Company> result = companyRepository.findAll(pageable);
return result;
}
#Override
public Page<Company> searchCompany(String companyName, Long companyGroupId, Pageable pageable) {
Page<Company> result = companyRepository.findByParametersWeb(companyName,companyGroupId,pageable);
return result;
}
#Override
public Optional<Company> findById(Long id) {
Optional<Company> entity = companyRepository.findById(id);
return entity;
}
#Override
public Company save(Company company) {
Company entity = companyRepository.save(company);
return entity;
}
#Override
public void delete(Long id) {
companyRepository.deleteById(id);
}
}
Testing Service class
class CompanyServiceImplTest {
#Mock
private CompanyRepository companyRepository;
private CompanyService companyService;
private Company company;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
companyService = new CompanyServiceImpl(companyRepository);
company = new Company();
company.setName("company");
company.setCompanyGroupId(1L);
}
#Test
void searchCompany() {
List<Company> companies = new ArrayList<>();
Pageable pageable= PageRequest.of(0,5);
Page<Company> result = new PageImpl<>(companies,pageable,1);
when(companyRepository.findByParametersWeb(anyString(),anyLong(),any(Pageable.class))).thenReturn(result);
Page<Company> newResult = companyService.searchCompany("giorgos",1L,pageable);
assertEquals(newResult.getTotalElements(),result.getTotalElements());
}
}
Finally My Company Repository
#Repository
public interface CompanyRepository extends JpaRepository<Company, Long> {
#Query("SELECT a FROM Company a WHERE (:name is null or ((a.name LIKE :name AND LENGTH(:name) > 0) OR ( a.name = '%')))")
List<Company> findByCompanyName(#Param("name") String name);
#Query("SELECT a FROM Company a WHERE (:name is null or (LENGTH(:name) > 0 " +
" AND ((:option = 'yes' AND a.name = :name) or (:option = 'start' AND a.name LIKE CONCAT(:name,'%')) " +
" or (:option = 'end' AND a.name LIKE CONCAT('%',:name)) or (a.name LIKE CONCAT('%',:name,'%'))))) " +
" AND (:companyGroupId is null or a.companyGroupId = :companyGroupId) ORDER BY a.name")
Page<Company> findByParametersWeb(String name,Long companyGroupId, Pageable pageable);
List<Company> findAllByNameOrderByName();
}
So you want to differentiate unit tests from integration or component tests here.
Your test would qualify as a unit test, it solely tests the functionality of your service layer isolated from everything else.
That is also why you mock your repository layer, to be independent from a database.
Contrary to that, integration and component tests test your whole application stack.
For this, the spring boot environment must be running, so you have to annotate your testclass with #ExtendWith(SpringExtension.class).
For these kind of tests you need an active db, so commonly you use a h2 database for your tests, that is filled with data prior to your tests. Have a look at this and this.
In the test itself you either inject your service and test from there, or you call your endpoints using RestTemplate.
#ExtendWith(SpringExtension.class)
#SpringBootTest
#Sql("/schema.sql")
public class DocumentManagementBackendApplicationTest {
#Autowired
private final CompanyServiceImpl companyServiceImpl;
#Test
#Sql("/searchCompany.sql")
public void testSearchCompany() {
List<Company> companies = new ArrayList<>();
Pageable pageable= PageRequest.of(0, 5);
Page<Company> result = companyService.searchCompany("giorgos",1L,pageable);
// now here you know, based on what you insert into your db with your sql scripts,
// what you should expect and so you can test for it
(...)
}
}
public class UserTransfer{
private String u_email;
private String u_password;
public UserTransfer(String u_email,String u_password) {
this.u_email=u_email;
this.u_password=u_password;
}
public String getU_email() {
return u_email;
}
public void setU_email(String u_email) {
this.u_email = u_email;
}
public String getU_password() {
return u_password;
}
public void setU_password(String u_password) {
this.u_password = u_password;
}
}
#Repository
public interface UserTransRepositiory extends JpaRepository<UserTransfer, String> {
#Query(value ="SELECT " + "new paddelec.backend.model.UserTransfer(u.u_email,u.u_password)"
+" FROM tokens t JOIN users u ON t.u_email=u.u_email WHERE t.token= ?1")
UserTransfer findByToken(String Token);
}
This is my Code I try to generate a UserTranfer Object. The regular TokenTransfer and User Repository works just fine. But here I am getting the "java.lang.IllegalArgumentException: Not a managed type: *.UserTransfer" Exception.
UserTransfer must be a JPA entity -> annotate the class with #Entity
->
#Entity
public class UserTransfer{
private String u_email;
private String u_password;
Moreover don't forget that each entity must have an #Id . Example : if the u_email is the primary key , annotate it with #Id
Using Spring Dat JPA, I need to query my database and return a range of OrderEntitys based on a startAmt and a endAmt of amounts. I'm not sure if I should map these two variables to entity OrderEntity, as fields in some type of separate class/entity/model, or simply declare them in my native query. Perhaps I should be using a service that implements EntityManager.createNativeQuery()?
Would like to do something like :
#Repository
public interface OrderRangeRepository extends JpaRepository<OrderEntity, OrderEntityID> {
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN startAmt AND endAmt;" , nativeQuery=true)
List<OrderEntity> findOrdersBy(int startAmt, int endAmt);
}
If I were to use EntityManager.createNativeQuery() in a service, perhaps something like below :
#Service
public class OrderRangeService {
#Autowired
EntityManager entityManager;
public List<OrderEntity> findAmountsBetween() {
List<OrderEntity> amountsBetween = entityManager.createNativeQuery("SELECT * FROM Orders WHERE Amount BETWEEN ?1 AND 2?;")
.setParameter(1, "startAmt")
.setParameter(2, "endAmt")
.getResultList();
return amountsBetween;
}
}
You can achieve this with Spring Data JPA without defining a native query.
#Repository
public interface OrderRangeRepository extends JpaRepository<OrderEntity, OrderEntityID> {
List<OrderEntity> findByAmountBetween(int startAmt, int endAmt);
}
If you want to use the native query change it to
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN :startAmt AND :endAmt" , nativeQuery=true)
List<OrderEntity> findOrdersBy(#Param("startAmt") int startAmt, #Param("endAmt") int endAmt);
You can invoke the query in a service by doing
#Service
public class OrderRangeService {
#Autowired
OrderRangeRepository orderRangeRepository ;
public List<OrderEntity> findAmountsBetween(int startAmt, int endAmt) {
List<OrderEntity> amountsBetween = orderRangeRepository.findByAmountBetween(startAmt, endAmt);
return amountsBetween;
}
}
Finally, from your controller, you should autowire the OrderRangeService and invoke the findAmountsBetween service method
#Autowired
OrderRangeService orderRangeService;
#GetMapping("/amountsFromAndTo")
#ResponseBody
public String getAmounts(#RequestParam int startAmt, #RequestParam int endAmt) {
List<OrderEntity> orderEntityL = orderRangeService.findAmountsBetween(startAmt, endAmt);
return orderEntityL.toString();
}
1. Named Parameters
Each parameter annotated with #Param must have a value string matching
the corresponding JPQL or SQL query parameter name. A query with named
parameters is easier to read and is less error-prone in case the query
needs to be refactored.
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN :startAmt AND :endAmt;" , nativeQuery=true)
List<OrderEntity> findOrdersBy(#Param("startAmt") int startAmt, #Param("endAmt") int endAmt);
}
2. Indexed Query Parameters
Spring Data will pass method parameters to the query in the same order
they appear in the method declaration
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN ?1 AND ?2;" , nativeQuery=true)
List<OrderEntity> findOrdersBy(int startAmt, int endAmt);
I am trying to re write the following query using entity manager..
public abstract class HibernateEntitySelector<T> extends HibernateDAOSupport implements EntitySelector<T> {
#Autowired
public void init(SessionFactory factory) {
setSessionFactory(factory);
}
public String SELECT_IDS = " IN (SELECT RESULT_ID FROM QUERY_RESULTS)";
public List<T> getEntitiesByIds(){
DetachedCriteria criteria = DetachedCriteria.forClass(getEntityClass());
criteria.add(Restrictions.sqlRestriction(getPrimaryKeyField()+SELECT_IDS));
return (List<T>) this.getHibernateTemplate().findByCriteria(criteria);
}
Something like this..
public abstract class HibernateEntitySelector<T> implements EntitySelector<T> {
public String SELECT_IDS = " IN (SELECT RESULT_ID FROM QUERY_RESULTS)";
#PersistenceContext
protected EntityManager em;
public List<T> getEntitiesByIds(){
String s = "FROM " + getEntityClass().getSimpleName() + " ent WHERE ent."+getEntityId()+SELECT_IDS;
Query query = this.em.createNamedQuery(s);
return (List<T>)query.getResultList();
}
}
But this fails due to QUERY_RESULTS not being mapped. Is there a way to do this without using the createNativeQuery method and then having to map all the columns manually?
You were quite close, though you are using:
Query query = this.em.createNamedQuery(s);
Use the createNativeQuery instead and alter the query string alongside also:
String s = "SELECT ent.* FROM " + getEntityClass().getSimpleName() + " ent WHERE ent."+getEntityId() + SELECT_IDS;
Query query = this.em.createNativeQuery(s, getEntityClass());
Try it out.
I'm trying to see how I would create a JPA Critera query which allows for fully dynamic filtering with multiple levels.
For example
select *
from table
where (
(column1 = 'A'
and
(column2 = 'B' or column3 = 'C')
)
or
column3 = 'D'
You need to create a Specification as in the test below. This can be dynamic.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=HelloWorldConfig.class)
public class SpecificationTest {
#Autowired
private AccountRepository repository;
#Test
public void test1() {
final List<String> names = Arrays.asList(new String[]{"George","Max"});
Specification<Account> specification = new Specification<Account>() {
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(root.get("name").in(names).not());
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
assertNotNull(repository);
repository.save(makeAccount("Greg", "123456787", "01-02-01"));
repository.save(makeAccount("George", "123456788", "01-02-02"));
repository.save(makeAccount("Max", "123456789", "01-02-03"));
List<Account> accounts = repository.findAll(specification);
assertEquals(1,accounts.size());
assertEquals("123456787",accounts.get(0).getAccountNumber());
}
private Account makeAccount(String name, String accountNumber, String sortCode) {
Account account = new Account();
account.setName(name);
account.setAccountNumber(accountNumber);
account.setSort(sortCode);
return account;
}
}
Where the repository looks like :
#Repository
public interface AccountRepository extends JpaRepository<Account, Long>, JpaSpecificationExecutor<Account> {
}