JPA predicate in queries - jpa

I have a route class, in this class, I defined a collection of locations.
#ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
#JoinTable(name = "route_location_map")
private List<Location> locations;
What I required is to check a particular location is present in my route
if (locationId != null && locationId.longValue() > 0) {
Expression<Collection<Long>> locations = route.get("locations").get("id");
predicate = builder.isMember(locationId, locations);
whereClauseList.add(predicate);
Unfortunately, this logic is not working, I didn't get any error too.
Can anybody help me to resolve this?

Post next your stacktrace. You need to use isMember() with your Entity or Expression.
public List<Route> findRoutes(Location location){
...
Expression<Collection<Location>> locations = route.get("locations");
predicate = builder.isMember(location, locations);
...
}

Related

efcore change modified state to update entity is not working with sub data objects

I set up a Generic repository using this code for update
private void AttachIfNot(TEntity entityToActive)
{
if (_dbContext.Entry(entityToActive).State == EntityState.Detached)
{
_dbSet.Attach(entityToActive);
}
}
private void UpdateEntity(TEntity entityToUpdate)
{
AttachIfNot(entityToUpdate);
_dbContext.Entry(entityToUpdate).State = EntityState.Modified;
}
It just attach the entity and set the modified state to save.
But when I use efocre ownsone to map a value object,the update entity function is not working.
I found out that it only works when I set Valueobject to modified too.
_dbContext.Entry(entityToUpdate).State = EntityState.Modified;
_dbContext.Entry(entityToUpdate.Valueobject).State = EntityState.Modified;
But It is hard for me to specify all the value objects in a Generic Repository.
This is code also has problems with one to many or other relations.
The working way is like this:
Classroom classroom = new Classroom
{
Id = 1,
Name = "b",
Students = new List<Student>
{
new Student()
{
Name = "aa",
Id = 2
}
}
};
if (_defaultDbContext.Entry(classroom).State == EntityState.Detached)
{
_defaultDbContext.Classrooms.Attach(classroom);
foreach(var stu in classroom.Students)
{
_defaultDbContext.Students.Attach(stu);
}
}
_defaultDbContext.Entry(classroom).State = EntityState.Modified;
foreach (var stu in classroom.Students)
{
_defaultDbContext.Entry(stu).State = EntityState.Modified;
}
_defaultDbContext.SaveChanges();
I found out one way is get the entity form repo then update it using automapper:
targetEntity = repo.GetById(entityId);
automapper.map(souceEntity,targetEntity);
//or
automapper.map(souceDto,targetEntity);
_dbContext.Save();
The entity comes by query, so the change will be tracked.
But I have to configure the automapper with this entity map when I want to change entity
CreateMap<EntityType, EntityType>();
I think it's not the best solution. Is there a bettere way?
DbContext.Update would be fine to fix this problem.
see:
https://www.learnentityframeworkcore.com/dbcontext/change-tracker

Datanucleus. Occasional javax.jdo.JDODetachedFieldAccessException: You have just attempted to access field "linkedObject"

We use Java Datanucleus 5.0.2 with JDO.
We get occasional exception while several threads are retrieving the same information and NO thread is changing this particular "linkedObject" reference.
NOTE: The object is retrieved using the fetch plan and logs show that.
It is hard to write a test case for this to fail as it is a race condition. But nevertheless I want to ask if someone has experienced it?
Caused by: javax.jdo.JDODetachedFieldAccessException: You have just attempted to access field "linkedObject" yet this field was not detached when you detached the object. Either dont access this field, or detach it when detaching the object.
at com.company.BaseClass.dnGetlinkedObject(BaseClass.java)
at com.company.BaseClass.getLinkedObject(BaseClass.java:71)
...
I can see three threads calling this method
private static <T> T getUniqueQueryJDO(final PersistenceManager pm, final JDOQLQuery query) throws PersistenceException {
try {
final javax.jdo.Query jdoQuery = setUpJDOQuery(pm, query);
jdoQuery.setUnique(true);
T result = null;
final T queryResult = (T) jdoQuery.executeWithMap(query.getMapValues());
if (queryResult != null) {
result = pm.detachCopy(queryResult);
}
jdoQuery.closeAll();
return result;
}
and one of them fails randomly
BaseClass.java
#PersistentDomainObject
#PersistenceCapable(table = "BaseClass", detachable = TRUE)
#Inheritance(strategy = InheritanceStrategy.SUPERCLASS_TABLE)
#FetchGroups(
...
#FetchGroup(name = FETCH_LINKED_OBJECT, members = {#Persistent(name = "linkedObject")})})
...
public class BaseClass {
...
public static final String FETCH_LINKED_CLASS = "FETCH_NAME";
...
#Persistent(defaultFetchGroup = FALSE, columns = {#Column(name = "linkedObjectId", allowsNull = FALSE)}, nullValue = NullValue.EXCEPTION)
private LinkedClass linkedObject;
...
public LinkedClass getLinkedObject() {
return linkedObject;
}
}

ASP.NET Core Entity Framework SQL Query SELECT

I am one of the many struggling to "upgrade" from ASP.NET to ASP.NET Core.
In the ASP.NET project, I made database calls from my DAL like so:
var result = context.Database.SqlQuery<Object_VM>("EXEC [sp_Object_GetByKey] #Key",
new SqlParameter("#Key", Key))
.FirstOrDefault();
return result;
My viewmodel has additional fields that my object does not, such as aggregates of related tables. It seems unnecessary and counter intuitive to include such fields in a database / table structure. My stored procedure calculates all those things and returns the fields as should be displayed, but not stored.
I see that ASP.NET Core has removed this functionality. I am trying to continue to use stored procedures and load view models (and thus not have the entity in the database). I see options like the following, but as a result I get "2", the number of rows being returned (or another mysterious result?).
using(context)
{
string cmd = "EXEC [sp_Object_getAll]";
var result = context.Database.ExecuteSQLCommand(cmd);
}
But that won't work because context.Database.ExecuteSQLCommand is only for altering the database, not "selecting".
I've also seen the following as a solution, but the code will not compile for me, as "set" is really set<TEntity>, and there isn't a database entity for this viewmodel.
var result = context.Set().FromSql("EXEC [sp_Object_getAll]");
Any assistance much appreciated.
Solution:
(per Tseng's advice)
On the GitHub Entity Framework Issues page, there is a discussion about this problem. One user recommends creating your own class to handle this sort of requests, and another adds an additional method that makes it run smoother. I changed the methods slights to accept slightly different params.
Here is my adaptation (very little difference), for others that are also looking for a solution:
Method in DAL
public JsonResult GetObjectByID(int ID)
{
SqlParameter[] parms = new SqlParameter[] { new SqlParameter("#ID", ID) };
var result = RDFacadeExtensions.GetModelFromQuery<Object_List_VM>(context, "EXEC [sp_Object_GetList] #ID", parms);
return new JsonResult(result.ToList(), setting);
}
Additional Class
public static class RDFacadeExtensions
{
public static RelationalDataReader ExecuteSqlQuery(
this DatabaseFacade databaseFacade,
string sql,
SqlParameter[] parameters)
{
var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
var rawSqlCommand = databaseFacade
.GetService<IRawSqlCommandBuilder>()
.Build(sql, parameters);
return rawSqlCommand
.RelationalCommand
.ExecuteReader(
databaseFacade.GetService<IRelationalConnection>(),
parameterValues: rawSqlCommand.ParameterValues);
}
}
public static IEnumerable<T> GetModelFromQuery<T>(
DbContext context,
string sql,
SqlParameter[] parameters)
where T : new()
{
DatabaseFacade databaseFacade = new DatabaseFacade(context);
using (DbDataReader dr = databaseFacade.ExecuteSqlQuery(sql, parameters).DbDataReader)
{
List<T> lst = new List<T>();
PropertyInfo[] props = typeof(T).GetProperties();
while (dr.Read())
{
T t = new T();
IEnumerable<string> actualNames = dr.GetColumnSchema().Select(o => o.ColumnName);
for (int i = 0; i < props.Length; ++i)
{
PropertyInfo pi = props[i];
if (!pi.CanWrite) continue;
System.ComponentModel.DataAnnotations.Schema.ColumnAttribute ca = pi.GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.Schema.ColumnAttribute)) as System.ComponentModel.DataAnnotations.Schema.ColumnAttribute;
string name = ca?.Name ?? pi.Name;
if (pi == null) continue;
if (!actualNames.Contains(name)) { continue; }
object value = dr[name];
Type pt = pi.DeclaringType;
bool nullable = pt.GetTypeInfo().IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>);
if (value == DBNull.Value) { value = null; }
if (value == null && pt.GetTypeInfo().IsValueType && !nullable)
{ value = Activator.CreateInstance(pt); }
pi.SetValue(t, value);
}//for i
lst.Add(t);
}//while
return lst;
}//using dr
}

How do you create a dynamic QueryDSL operator based on a quoted String value

I have a pojo that contains a property name, logic operator as String and the value of property. What I want to accomplish is create a Predicate or Expression etc dynamically from the pojo data. Below are my code:
public class QueryParam {
private String property = "acctType"; //can be any property of classname
private String operator = "eqic" //can be any logic operator !=, >, <, >=, <= etc
private Object value; //will store the value of
// getters/setters here
}
public interface CustomerRepository extends JpaRepository<Customer, Long>, QueryDslPredicateExecutor<Customer>{
}
#Service("CustomerService")
class MyCustomerServiceImpl {
#Resource
private CustomerRepository custRpstry;
//if classname is Customer, property is "acctType", operator is "eqic", and value is "Corporate"
//I want my findAll below to retrieve all Customers having acctType = "Corporate"
List<Customer> findAll(List<QueryParam> qryParam) {
QCustomer qcust = QCustomer.customer;
BooleanBuilder where = new BooleanBuilder();
for(QueryParam param : qryParam) {
//within this block, i want a BooleanBuilder to resolve to:
where.and(qcust.acctType.equalsIgnoreCase("Corporate"));
something like:
where.and(param.getClassname().param.getProperty().param.getOperator().param.getValue())
}
return custRpstry.findAll(where.getValue()).getContent();
}
}
I can't figure out to formulate my BooleanBuilder especially the portion that will convert
getOperator() into .equalIgnoreCase().
Any help would be greatly appreciated.
Thanks in advance,
Mario
After combining several answers to some related questions here in so, I was able to formulate a solution that works for me.
BooleanBuilder where = new BooleanBuilder();
for(QueryParam param: qryParam) {
//create: Expressions.predicate(Operator<Boolean> opr, StringPath sp, filter value)
//create an Operator<Boolean>
Operator<Boolean> opr = OperationUtils.getOperator(param.getOperator().getValue());
//create a StringPath to a class' property
Path<User> entityPath = Expressions.path(Customer.class, "customer");
Path<String> propPath = Expressions.path(String.class, entityPath, param.getProperty());
//create Predicate expression
Predicate predicate = Expressions.predicate(opr, propPath, Expressions.constant(param.getValue()));
where.and(predicate);
}
list = repository.findAll(where.getValue(), pageReq).getContent();
My OperationUtils.java
public class OperationUtils {
public static com.mysema.query.types.Operator<Boolean> getOperator(String key) {
Map<String, com.mysema.query.types.Operator<Boolean>> operators = ImmutableMap.<String, com.mysema.query.types.Operator<Boolean>>builder()
.put(Operator.EQ.getValue() ,Ops.EQ)
.put(Operator.NE.getValue() ,Ops.NE)
.put(Operator.GT.getValue() ,Ops.GT)
.put(Operator.GTE.getValue() ,Ops.GOE)
.put(Operator.LT.getValue() ,Ops.LT)
.put(Operator.LTE.getValue() ,Ops.LOE)
.build();
return operators.get(key);
}
}

Entity Framework eager loading/"IncludeEverything()"?

In EF4, I have a small object map and the volume of data is also small. So for queries I want to eager load all associated data. Is there any single method call that can do the job, like "IQueryable.IncludeEverything()", rather than call Include() repeatedly with hardcoded property names?
There's nothing out of the box, but you could use MetadataWorkspace to implement it:
public static IQueryable<T> IncludeEverything<T>(this IQueryable<T> query, ObjectContext context)
where T : class
{
var ospaceEntityType = context.MetadataWorkspace.GetItem<EntityType>(
typeof(T).FullName, DataSpace.OSpace);
var cspaceEntityType = context.MetadataWorkspace.GetEdmSpaceType(ospaceEntityType);
var includedTypes = new HashSet<EdmType>();
includedTypes.Add(cspaceEntityType);
return IncludeEverything(query, cspaceEntityType as EntityType, "", includedTypes);
}
private static IQueryable<T> IncludeEverything<T>(IQueryable<T> query,
EntityType entity,
string path,
HashSet<EdmType> includedTypes)
where T : class
{
foreach (var navigationProperty in entity.NavigationProperties)
{
var propertyEdmType = navigationProperty.TypeUsage.EdmType;
if (includedTypes.Contains(propertyEdmType))
{
continue;
}
includedTypes.Add(propertyEdmType);
var propertyCollectionType = propertyEdmType as CollectionType;
EntityType propertyEntityType;
if (propertyCollectionType != null)
{
propertyEntityType = propertyCollectionType.TypeUsage.EdmType as EntityType;
} else
{
propertyEntityType = propertyEdmType as EntityType;
}
var propertyPath = string.IsNullOrEmpty(path) ? "" : path + ".";
propertyPath += navigationProperty.Name;
query = query.Include(propertyPath);
query = IncludeEverything(query, propertyEntityType, propertyPath, includedTypes);
}
return query;
}
Note that this code is just for illustration. It doesn't have parameter validation, it may include the same enities several times and it won't include all the related entities if there are cycles in your model.