How to unit test a jpa repository method? - spring

I have coded a JPA repository method and I am now realizing it is impossible to unit test.
Can anyone please advise how to unit test the following method or how to refactor my repository so that it is unit-testable?
Here is the problematic method:
#Override
public List<Pli> findPlisByMultiField(String identifiant, Date dateReceptionFrom, Date dateReceptionTo, PaiementEnum paiement, AREnum ar, String numeroAR, FDVEnum FDV, ConteneurNum conteneurNum, StatutPli statut) {
log.debug("findPlisByMultiField");
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Pli> c = criteriaBuilder.createQuery(Pli.class);
Root<Pli> pli = c.from(Pli.class);
List<Predicate> criteria = new ArrayList<Predicate>();
if (identifiant != null && !identifiant.trim().equals("")) {
ParameterExpression<String> parameterIdentifiant = criteriaBuilder.parameter(String.class, "identifiant");
Predicate conditionIdentifiant = criteriaBuilder.like(pli.<String> get("identifiant"), parameterIdentifiant);
criteria.add(conditionIdentifiant);
}
if (dateReceptionFrom != null && dateReceptionTo != null) {
ParameterExpression<Date> parameterDateReceptionFrom = criteriaBuilder.parameter(Date.class, "dateReceptionFrom");
ParameterExpression<Date> parameterDateReceptionTo = criteriaBuilder.parameter(Date.class, "dateReceptionTo");
Predicate conditionDateReception = criteriaBuilder.between(pli.<Date> get("dateReception"), parameterDateReceptionFrom, parameterDateReceptionTo);
criteria.add(conditionDateReception);
}
if (paiement != null) {
if (paiement.equals(PaiementEnum.IsPaiement)) {
Predicate conditionPaiementEnum = criteriaBuilder.equal(pli.<PaiementEnum> get("paiement"), true);
criteria.add(conditionPaiementEnum);
} else {
Predicate conditionPaiementEnum = criteriaBuilder.equal(pli.<PaiementEnum> get("paiement"), false);
criteria.add(conditionPaiementEnum);
}
}
if (ar != null) {
if (ar.equals(AREnum.IsAR)) {
Predicate conditionAREnum = criteriaBuilder.equal(pli.<AREnum> get("AR"), true);
criteria.add(conditionAREnum);
} else {
Predicate conditionAREnum = criteriaBuilder.equal(pli.<AREnum> get("AR"), false);
criteria.add(conditionAREnum);
}
}
if (numeroAR != null && !numeroAR.trim().equals("")) {
ParameterExpression<String> parameterNumeroAR = criteriaBuilder.parameter(String.class, "numeroAR");
Predicate conditionNumeroAR = criteriaBuilder.like(pli.<String> get("numeroAR"), parameterNumeroAR);
criteria.add(conditionNumeroAR);
}
if (FDV != null) {
if (FDV.equals(FDVEnum.IsFDV)) {
Predicate conditionFDVEnum = criteriaBuilder.equal(pli.<FDVEnum> get("FDV"), true);
criteria.add(conditionFDVEnum);
} else {
Predicate conditionFDVEnum = criteriaBuilder.equal(pli.<FDVEnum> get("FDV"), false);
criteria.add(conditionFDVEnum);
}
}
if (conteneurNum != null) {
ParameterExpression<ConteneurNum> parameterConteneurNum = criteriaBuilder.parameter(ConteneurNum.class, "conteneurNum");
Predicate conditionConteneurNum = criteriaBuilder.equal(pli.<ConteneurNum> get("conteneurNum"), parameterConteneurNum);
criteria.add(conditionConteneurNum);
}
if (statut != null) {
ParameterExpression<StatutPli> parameterStatut = criteriaBuilder.parameter(StatutPli.class, "statut");
Predicate conditionStatut = criteriaBuilder.equal(pli.<StatutPli> get("statut"), parameterStatut);
criteria.add(conditionStatut);
}
if (criteria.size() == 0) {
return Pli.findAllPlis();
} else if (criteria.size() == 1) {
c.where(criteria.get(0));
} else {
c.where(criteriaBuilder.and(criteria.toArray(new Predicate[0])));
}
TypedQuery<Pli> q = em.createQuery(c);
if (identifiant != null && !identifiant.trim().equals("")) {
q.setParameter("identifiant", "%" + identifiant + "%");
}
if (dateReceptionFrom != null && dateReceptionTo != null) {
q.setParameter("dateReceptionFrom", dateReceptionFrom);
q.setParameter("dateReceptionTo", dateReceptionTo);
}
if (numeroAR != null && !numeroAR.trim().equals("")) {
q.setParameter("numeroAR", "%" + numeroAR + "%");
}
if (conteneurNum != null) {
q.setParameter("conteneurNum", conteneurNum);
}
if (statut != null) {
q.setParameter("statut", statut);
}
return q.getResultList();
}

Well, I don't think you will be able to Unit Test it as it's strictly specified, but you can make a test using an in memory database (look out for HSQL) so that the app don't need to actually go to the real database just for testing.
That way you will be able to create an automated test that could run inside JUnit for example, mocking maybe only some of the methods.

Related

duplicate result of criteria Api in join query using JpaSpecification

i am new to criteria api, so i have two models: GithubRepository and Issue where GithubReposity has many issue and every issue has one GithubRepository.
I have created many filters but the filter that did not work is the filter for GithubRepository where i want to check if a githubRepository has issues or Not.
after executing and applying has issues unfortunately something wrong happened and i see many duplicates.
here what i did in RepoSpecification:
public Specification<GithubRepository> getReposByIdIn(List<Long> iDs, String repoName) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (repoName != null && !repoName.isEmpty()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("repositoryName")),
"%" + repoName.toLowerCase() + "%"));
}
if (iDs != null && !iDs.isEmpty()) {
predicates.add( criteriaBuilder.in(root.get("id")).value(iDs) );
}
query.orderBy(criteriaBuilder.asc(root.get("repositoryName")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
public Specification<GithubRepository> getRepos(Boolean hasIssues,Boolean IsPrivate , String repoName) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (repoName != null && !repoName.isEmpty()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("repositoryName")),
"%" + repoName.toLowerCase() + "%"));
}
if ( hasIssues!= null && hasIssues.equals(true)) {
// JOINS INSTANCE
Join<GithubRepository, Issue> repoIssueJoin = root.join("issues");
//Predicate predicate = criteriaBuilder.equal(repoIssueJoin.get("githubRepository").get("repositoryName"),
repoName);
//predicates.add(criteriaBuilder.isNotNull(repoIssueJoin));
predicates.add(criteriaBuilder.equal(repoIssueJoin.get("githubRepository"),root));
}
if (IsPrivate != null && IsPrivate.equals(true)) {
predicates.add(criteriaBuilder.equal(root.get("isPrivate"), true));
}
query.orderBy(criteriaBuilder.asc(root.get("repositoryName")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
my problem is in here exactly:
if ( hasIssues!= null && hasIssues.equals(true)) {
// JOINS INSTANCE
Join<GithubRepository, Issue> repoIssueJoin = root.join("issues");
//Predicate predicate = criteriaBuilder.equal(repoIssueJoin.get("githubRepository").get("repositoryName"),
repoName);
//predicates.add(criteriaBuilder.isNotNull(repoIssueJoin));
predicates.add(criteriaBuilder.equal(repoIssueJoin.get("githubRepository"),root));
}
i do not know what is wrong exactly and how to fix the problem but i am stacked with it because i am planning to add a new filter to filter greatOrEqual by number of issues

How to handle null Pointer Exception after applying Spring data query?

I have below snippet of code.
TestMsgCenter msgLog = TestMsgCenter.findByIp("localhost")
if (msgLog != null) {
msgLog.setClothDate(new Date())
msgLog.merge()
} else {
msgLog.setClothType("Lets")
msgLog.setClothDate(new Date())
msgLog.setClothId("Go")
msgLog.save()
}
How to handle null case in above else block?
You are trying to assign a values to a null object.
You need to instantiate one:
TestMsgCenter msgLog = TestMsgCenter.findByIp("localhost")
if (msgLog != null) {
msgLog.setClothDate(new Date())
msgLog.merge()
} else {
msgLog = new MsgLog(); //here
msgLog.setClothType("Lets")
msgLog.setClothDate(new Date())
msgLog.setClothId("Go")
msgLog.save()
}

LINQ query fails with nullable variable ormlite

I'm trying to write following LINQ query using ServiceStack Ormlite.
dbConn.Select<Product>(p => p.IsActive.HasValue && p.IsActive.Value)
Here, Product is my item class and "IsActive" is Nullable Bool property in that class. When this line executes it always throws "InvalidOperationException" with the message
variable 'p' of type '' referenced from scope '', but it is not defined
I tried different variants as following but still same exception result
dbConn.Select<Product>(p => p.IsActive.HasValue == true && p.IsActive.Value == true)
dbConn.Select<Product>(p => p.IsActive != null && p.IsActive.Value == true)
But if I just write
dbConn.Select<Product>(p => p.IsActive.HasValue)
then it works.
I'm puzzled what is the problem? Is this servicestack ormlite issue?
My answer can handle Nullable value like "value" and "HasValue" with servicestack ormlite. And But also with datetime nullable ,like 'createdate.value.Year'.
you must change two place.
modify VisitMemberAccess method:
protected virtual object VisitMemberAccess(MemberExpression m)
{
if (m.Expression != null)
{
if (m.Member.DeclaringType.IsNullableType())
{
if (m.Member.Name == nameof(Nullable<bool>.Value))
return Visit(m.Expression);
if (m.Member.Name == nameof(Nullable<bool>.HasValue))
{
var doesNotEqualNull = Expression.NotEqual(m.Expression, Expression.Constant(null));
return Visit(doesNotEqualNull); // Nullable<T>.HasValue is equivalent to "!= null"
}
throw new ArgumentException(string.Format("Expression '{0}' accesses unsupported property '{1}' of Nullable<T>", m, m.Member));
}
if (m.Member.DeclaringType == typeof(DateTime))
{
var ExpressionInfo = m.Expression as MemberExpression;
if (ExpressionInfo.Member.DeclaringType.IsNullableType())
{
if (ExpressionInfo.Member.Name == nameof(Nullable<bool>.Value))
{
var modelType = (ExpressionInfo.Expression as MemberExpression).Expression.Type;
var tableDef = modelType.GetModelDefinition();
var columnName = (ExpressionInfo.Expression as MemberExpression).Member.Name;
var QuotedColumnName = GetQuotedColumnName(tableDef, columnName);
if (m.Member.Name == "Year")
{
return new PartialSqlString(string.Format("DATEPART(yyyy,{0})", QuotedColumnName));
}
if (m.Member.Name == "Month")
return new PartialSqlString(string.Format("DATEPART(mm,{0})", QuotedColumnName));
}
if (ExpressionInfo.Member.Name == nameof(Nullable<bool>.HasValue))
{
var doesNotEqualNull = Expression.NotEqual(ExpressionInfo.Expression, Expression.Constant(null));
return Visit(doesNotEqualNull); // Nullable<T>.HasValue is equivalent to "!= null"
}
}
else
{
var modelType = ExpressionInfo.Expression.Type;
var tableDef = modelType.GetModelDefinition();
var columnName = ExpressionInfo.Member.Name;
var QuotedColumnName = GetQuotedColumnName(tableDef, columnName);
if (m.Member.Name == "Year")
return new PartialSqlString(string.Format("DATEPART(yyyy,{0})", QuotedColumnName));
if (m.Member.Name == "Month")
return new PartialSqlString(string.Format("DATEPART(mm,{0})", QuotedColumnName));
}
}
if (m.Expression.NodeType == ExpressionType.Parameter || m.Expression.NodeType == ExpressionType.Convert)
{
var propertyInfo = (PropertyInfo)m.Member;
var modelType = m.Expression.Type;
if (m.Expression.NodeType == ExpressionType.Convert)
{
var unaryExpr = m.Expression as UnaryExpression;
if (unaryExpr != null)
{
modelType = unaryExpr.Operand.Type;
}
}
var tableDef = modelType.GetModelDefinition();
if (propertyInfo.PropertyType.IsEnum)
return new EnumMemberAccess(
GetQuotedColumnName(tableDef, m.Member.Name), propertyInfo.PropertyType);
return new PartialSqlString(GetQuotedColumnName(tableDef, m.Member.Name));
}
}
var member = Expression.Convert(m, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
return getter();
}
modify VisitLambda method :
protected virtual object VisitLambda(LambdaExpression lambda)
{
if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
{
MemberExpression m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
string r = VisitMemberAccess(m).ToString();
if (m.Member.DeclaringType.IsNullableType())
return r;
return string.Format("{0}={1}", r, GetQuotedTrueValue());
}
}
return Visit(lambda.Body);
}
This is nature of the Linq. In order to achieve what you need, you will need to use two where closes:
dbConn.Where<Product>(p => p.IsActive.HasValue).Where(p=>p.Value==true);

ArrayIndex is not supported in LINQ to Entities

// update information from this reponse to database
public bool UpdatePassengerInformation(GetResponse response)
{
try
{
using (MyEntities context = new MyEntities())
{
var masterID = (from Id in context.BookMaster where Id.BookingId == response.BookingId select Id.MasterId).First();
if (masterID != null)
{
for (int i = 0; i < response.Book.Length; i++)
{
var passenger_info = from passenger in context.BookPassenger
where passenger.BookID == masterID &&
assenger.FirstName == response.Book[i].FirstName && passenger.LastName == response.Book[i].LastName select passenger;
// in below loop i got exception "The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities."
foreach (BookPassenger book_passenger in passenger_info)
{
book_passenger.TicketId = response.Book[i].TicketId;
book_passenger.TicketNumber = response.Book[i].TicketNumber;
}
}
context.SaveChanges();
}
}
return true;
}
catch (Exception ex)
{
throw ex;
}
}
So don't use it in query, just assign a new variable and use this one.
for (int i = 0; i < response.Book.Length; i++)
{
var responseBook = response.Book[i];
var passenger_info = from passenger in context.BookPassenger
where passenger.BookID == masterID &&
assenger.FirstName == responseBook.FirstName && passenger.LastName == responseBook.LastName select passenger;
foreach (BookPassenger book_passenger in passenger_info)
{
book_passenger.TicketId = responseBook.TicketId;
book_passenger.TicketNumber = responseBook.TicketNumber;
}
}

LINQ TO SQL, Dynamic query with DATE type fields

I'm building a query with the LINQ dynamic library so I don't know how many potential parameters will I have and I get an error when trying to query DATE type fields:
Operator '>=' incompatible with operand types 'DateTime' and 'String'
When I step through the debugger in the Dynamic.cs it shows that the value is of type string and the field is of type date so the problem is obvious but I have no idea how to approach it.
Any ideas?
BR
Code:
using (MyEntities db = new MyEntities())
{
String SQLparam = "CreateDate >= \"" + DateTime.Now.ToShortDateString() + "\"";
List<UserList> UserList = db.UserList.Where(SQLparam).ToList();
}
You have to use a parameterized query, e.g.
using (MyEntities db = new MyEntities())
{
String SQLparam = "CreateDate >= #1";
List<UserList> UserList = db.UserList.Where(SQLparam, new [] { DateTime.Now }).ToList();
}
I had the same problem, but with Boolean as well, so I generalised the solution
Expression ConstantParser<T>(Expression left, Token op, Expression right, Func<string, T> parser)
{
if (right is ConstantExpression)
{
try
{
var value = ((ConstantExpression)right).Value.ToString();
return Expression.Constant(parser(value));
}
catch (Exception)
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
The lines in the main function then become...
else if (left.Type == typeof(DateTime) && right.Type == typeof(string))
{
right = this.ConstantParser(left, op, right, DateTime.Parse);
}
else if (left.Type == typeof(DateTime?) && right.Type == typeof(string))
{
right = this.ConstantParser(left, op, right, x => { DateTime? t = DateTime.Parse(x); return t; });
}
else if (left.Type == typeof(Boolean) && right.Type == typeof(string))
{
right = this.ConstantParser(left, op, right, Boolean.Parse);
}
Only disadvantage I can see to this approach is that if the Parse fails, we will raise and exception, but given that we throw one anyway, I don't see that it matters too much
I was in the same boat and I was able to solve this by changing one method in the Dynamic Library. It's a hack, but it allows me to use dates in expressions with equality operators (=,>,<, etc..).
I'm posting the code here in case someone still cares.
The code I added is in the if block around line 53
else if (left.Type == typeof(DateTime) && right.Type == typeof(string))
{
if (right is ConstantExpression)
{
DateTime datevalue;
string value = ((ConstantExpression) right).Value.ToString();
if (DateTime.TryParse(value, out datevalue))
{
right = Expression.Constant(datevalue);
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
The code basically checks if you are trying to compare a date field (left) with a string (right). And then it converts the right expression to a date constant
Here is the whole ParseComparison method:
// =, ==, !=, <>, >, >=, <, <= operators
Expression ParseComparison()
{
Expression left = ParseAdditive();
while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual ||
token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater ||
token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual ||
token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual)
{
Token op = token;
NextToken();
Expression right = ParseAdditive();
bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual ||
op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater;
if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType)
{
if (left.Type != right.Type)
{
if (left.Type.IsAssignableFrom(right.Type))
{
right = Expression.Convert(right, left.Type);
}
else if (right.Type.IsAssignableFrom(left.Type))
{
left = Expression.Convert(left, right.Type);
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
}
else if (IsEnumType(left.Type) || IsEnumType(right.Type))
{
if (left.Type != right.Type)
{
Expression e;
if ((e = PromoteExpression(right, left.Type, true)) != null)
{
right = e;
}
else if ((e = PromoteExpression(left, right.Type, true)) != null)
{
left = e;
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
}
else if (left.Type == typeof(DateTime) && right.Type == typeof(string))
{
if (right is ConstantExpression)
{
DateTime datevalue;
string value = ((ConstantExpression) right).Value.ToString();
if (DateTime.TryParse(value, out datevalue))
{
right = Expression.Constant(datevalue);
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
else
{
CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
op.text, ref left, ref right, op.pos);
}
switch (op.id)
{
case TokenId.Equal:
case TokenId.DoubleEqual:
left = GenerateEqual(left, right);
break;
case TokenId.ExclamationEqual:
case TokenId.LessGreater:
left = GenerateNotEqual(left, right);
break;
case TokenId.GreaterThan:
left = GenerateGreaterThan(left, right);
break;
case TokenId.GreaterThanEqual:
left = GenerateGreaterThanEqual(left, right);
break;
case TokenId.LessThan:
left = GenerateLessThan(left, right);
break;
case TokenId.LessThanEqual:
left = GenerateLessThanEqual(left, right);
break;
}
}
return left;
}
Because I had to make a comparison for a DateTime? and I had make this modification.
else if (left.Type == typeof(DateTime?) && right.Type == typeof(string))
{
if (right is ConstantExpression)
{
DateTime datevalue;
string value = ((ConstantExpression)right).Value.ToString();
if (DateTime.TryParse(value, out datevalue))
{
DateTime? nullableDateValue = datevalue;
right = Expression.Constant(nullableDateValue, typeof(DateTime?));
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
else
{
throw IncompatibleOperandsError(op.text, left, right, op.pos);
}
}
Thanks for the tip!

Resources