Why JPQL returns only first and last field in query result using AS? - eclipselink

I have a model:
public class SomeModel extends Model {
private String name;
private String description;
private String comment;
... a lot of other fields
private String note;
}
I want to get the list of models from database as list of maps with only needed fields. I'm doing it following way:
EntityManager em = GuiceConfigSingleton.inject(EntityManager.class);
Query query = em.createQuery("SELECT
o.name AS ModelName,
o.description AS ModelDescription,
o.comment AS ModelComment,
o.note AS ModelNote
FROM SomeModel o");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
Resultlist of this query is a list of maps. And every map contains only first(ModelName) and last(ModelNote) field (other fields is not empty in database). When I'm doing it without "as" using just:
Query query = em.createQuery("SELECT
o.name,
o.description,
o.comment,
o.note
FROM SomeModel o");
I got all fields but without proper keys. What's wrong?
Screenshots with simplified example:
With "AS": http://imgur.com/bKZnqSx
Without "AS": http://imgur.com/PieCRzg
UPDATE:
It was a bug in EclipseLink 2.5.0. With 2.5.2 everything is ok!

there is an other way to fetch these fields
Query query = entityManager.createNativeQuery("your existing query");
final List<Object[]> rsList = (List<Object[]>) query.getResultList();
for (Object[] objects : rsList) {
String modelName = objects[0] == null ? ""
: objects[0].toString();
String modelDesc = objects[1] == null ? ""
: objects[1].toString();
String comment = objects[2] == null ? ""
: objects[2].toString();
String note = objects[3] == null ? ""
: objects[3].toString();
}
I hope it helps.

It is a bug in 2.5.0 version of EclipseLink. Since 2.5.1 everything is ok!
Lesson learned: always check for the last release.

Related

CriteriaBuilder.size() and Hibernate's #Where annotation

I have the following setup:
#Entity
public class Function {
private String name;
#OneToMany(mappedBy = "function", cascade = CascadeType.ALL, orphanRemoval = true)
#Where(clause = "type = 'In'") // <=== seems to cause problems for CriteriaBuilder::size
private Set<Parameter> inParameters = new HashSet<>();
#OneToMany(mappedBy = "function", cascade = CascadeType.ALL, orphanRemoval = true)
#Where(clause = "type = 'Out'") // <=== seems to cause problems for CriteriaBuilder::size
private Set<Parameter> outParameters = new HashSet<>();
}
#Entity
public class Parameter {
private String name;
#Enumerated(EnumType.STRING)
private ParameterType type;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "function_id")
private Function function;
}
The overall problem I am trying to solve is find all functions that have outParameters with an exact dynamic set of names. E.g. find all function with outParameters whose names are exactly ('outParam1', 'outParam2')
This seems to be an "exact relational division" problem in SQL, so there might be better solutions out there, but the way I've gone about doing it is like this:
List<String> paramNames = ...
Root<Function> func = criteria.from(Function.class);
Path outParams = func.get("outParameters");
Path paramName = func.join("outParameters").get("name");
...
// CriteriaBuilder Code
builder.and(
builder.or(paramNames.stream().map(name -> builder.like(builder.lower(paramName), builder.literal(name))).toArray(Predicate[]::new)),
builder.equal(builder.size(outParams), paramNames.size()));
The problem I get is that the builder.size() does not seem to take into account the #Where annotation. Because the "CriteriaBuilder code" is nested in a generic Specification that should work for any type of Entity, I am not able to simply add a query.where() clause.
The code works when a function has 0 input parameters, but it does not work when it has more. I have taken a look at the SQL that is generated and I can see that it's missing:
SELECT DISTINCT
function0_.id AS id1_37_,
function0_.name AS name4_37_,
FROM
functions function0_
LEFT OUTER JOIN parameters outparamet2_ ON function0_.id = outparamet2_.function_id
AND (outparamet2_.type = 'Out') -- <== where clause added here
WHERE (lower(outparamet2_.name)
LIKE lower(?)
OR lower(outparamet2_.name)
LIKE lower(?))
AND (
SELECT
count(outparamet4_.function_id)
FROM
parameters outparamet4_
WHERE
function0_.id = outparamet4_.function_id) = 2 -- <== where clause NOT added here
Any help appreciated (either with a different approach to the problem, or with a workaround to builder.size() not working).
The where annotation is in the function entity, in the subquery you have not used that entity so the operation is correct, try using the function entity as root of the subquery, or to implement the where manually.
For the next one, it would be recommended that you include the complete Criteria API code to be more precise in the answers.

Ignite SqlQuery for complex java objects

In my cache I have a complex java object as below -
class Person{
private Department d;
....
}
class Department {
private Department code;
....
}
I am using below SQLQuery to read it -
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "d.code = ?");
String args="101"; // department code
QueryCursor<Cache.Entry<Short, BinaryObject>> resultSet = personCache.query(query.setArgs(args))
I am getting below error -
Caused by: class org.apache.ignite.internal.processors.query.IgniteSQLException: Failed to parse query: SELECT "PERSON_CACHE"."PERSONENTITY"._KEY, "TPERSON_CACHE"."PERSONENTITY"._VAL FROM "PERSON_CACHE"."PERSONENTITY" WHERE id.code = ?
Am I doing anything wrong here ?
You can access nested fields, but only if they were configured with QuerySqlField annotation in advance:
class Person{
private Department d;
...
}
class Department {
#QuerySqlField
private Department code;
....
}
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "code = ?");
Destructuring is not supported by Ignite SQL and there are no solid plans to implement it.
This means you can't peek into fields that are rich objects, maps, lists, etc. You should introduce a departmentId numeric field here.
Theoretically you could also try putting #QuerySqlField annotation on Department's field code, and then access it as CODE = ?. Your mileage may vary. I for one would like to hear about the result of such experiment.
I resolved it by using predicate.
IgniteBiPredicate<Long, BinaryObject> predicate = new IgniteBiPredicate<Long, BinaryObject>() {
#Override
public boolean apply(Long e1, BinaryObject e2) {
Person p= e2.deserialize();
short s = (short) args[0];
return p.getId().getCode == s;
}
};

Is it possible to cast a SqlQuery results from Ebean to a Model (Bean)?

Using Ebean (via Play Framework 2.1.1), I have to build a home made SQL query, but I'd like it to return directly a List<MyModel>, instead of a List<SqlRow> that I would have to query the database for each id from the result to have a List<MyModel>.
Is it possible to cast directly a SqlQuery/SqlRow to a Model ?
Actually, I do that :
SqlQuery query = Ebean.createSqlQuery("SELECT id FROM MyModel WHERE ...");
List<SqlRow> rows = query.findList(); // not directly possible.
List<MyModel> results = new ArrayList<MyModel>();
for (SqlRow row : rows) {
results.add(MyModel.find.idEq(row.getLong("id)));
}
Ideally, it would be something like :
SqlQuery query = Ebean.createSqlQuery("SELECT id FROM MyModel WHERE ...");
List<MyModel> results = (List<MyModel>) query.findList(); // not directly possible.
This is not very efficient.
Try this:
RawSql rawSql = RawSqlBuilder.parse("SELECT id, value, ... FROM MyModel WHERE ...")
.columnMapping("id", "id")
.columnMapping("value", "value")
//...
.create();
Query<MyModel> query = Ebean.find(MyModel.class);
query.setRawSql(rawSql);
List<MyModel> result = query.findList();
Or using the finder:
Finder<Long, MyModel> finder = new Finder<Long, MyModel>(Long.class, MyModel.class);
List<MyModel> result = finder.where()
.eq("name", "Barack Obama")
//...
.findList();

Is there a way to do a "in" statment in javax.persistence.Query [duplicate]

I have the following parametrised JPA, or Hibernate, query:
SELECT entity FROM Entity entity WHERE name IN (?)
I want to pass the parameter as an ArrayList<String>, is this possible? Hibernate current tells me, that
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
Is this possible at all?
ANSWER: Collections as parameters only work with named parameters like ":name", not with JDBC style parameters like "?".
Are you using Hibernate's Query object, or JPA? For JPA, it should work fine:
String jpql = "from A where name in (:names)";
Query q = em.createQuery(jpql);
q.setParameter("names", l);
For Hibernate's, you'll need to use the setParameterList:
String hql = "from A where name in (:names)";
Query q = s.createQuery(hql);
q.setParameterList("names", l);
in HQL you can use query parameter and set Collection with setParameterList method.
Query q = session.createQuery("SELECT entity FROM Entity entity WHERE name IN (:names)");
q.setParameterList("names", names);
Leaving out the parenthesis and simply calling 'setParameter' now works with at least Hibernate.
String jpql = "from A where name in :names";
Query q = em.createQuery(jpql);
q.setParameter("names", l);
Using pure JPA with Hibernate 5.0.2.Final as the actual provider the following seems to work with positional parameters as well:
Entity.java:
#Entity
#NamedQueries({
#NamedQuery(name = "byAttributes", query = "select e from Entity e where e.attribute in (?1)") })
public class Entity {
#Column(name = "attribute")
private String attribute;
}
Dao.java:
public class Dao {
public List<Entity> findByAttributes(Set<String> attributes) {
Query query = em.createNamedQuery("byAttributes");
query.setParameter(1, attributes);
List<Entity> entities = query.getResultList();
return entities;
}
}
query.setParameterList("name", new String[] { "Ron", "Som", "Roxi"}); fixed my issue

Does Dapper support the like operator?

Using Dapper-dot-net...
The following yields no results in the data object:
var data = conn.Query(#"
select top 25
Term as Label,
Type,
ID
from SearchTerms
WHERE Term like '%#T%'",
new { T = (string)term });
However, when I just use a regular String Format like:
string QueryString = String.Format("select top 25 Term as Label, Type, ID from SearchTerms WHERE Term like '%{0}%'", term);
var data = conn.Query(QueryString);
I get 25 rows back in the collection. Is Dapper not correctly parsing the end of the parameter #T?
Try:
term = "whateverterm";
var encodeForLike = term => term.Replace("[", "[[]").Replace("%", "[%]");
string term = "%" + encodeForLike(term) + "%";
var data = conn.Query(#"
select top 25
Term as Label,
Type,
ID
from SearchTerms
WHERE Term like #term",
new { term });
There is nothing special about like operators, you never want your params inside string literals, they will not work, instead they will be interpreted as a string.
note
The hard-coded example in your second snippet is strongly discouraged, besides being a huge problem with sql injection, it can cause dapper to leak.
caveat
Any like match that is leading with a wildcard is not SARGable, which means it is slow and will require an index scan.
Yes it does. This simple solution has worked for me everytime:
db.Query<Remitente>("SELECT *
FROM Remitentes
WHERE Nombre LIKE #n", new { n = "%" + nombre + "%" })
.ToList();
Best way to use this to add concat function in query as it save in sql injecting as well, but concat function is only support above than sql 2012
string query = "SELECT * from country WHERE Name LIKE CONCAT('%',#name,'%');"
var results = connection.query<country>(query, new {name});
The answer from Sam wasn't working for me so after some testing I came up with using the SQLite CONCAT equivalent which seems to work:
string sql = "SELECT * FROM myTable WHERE Name LIKE '%' || #NAME || '%'";
var data = IEnumerable data = conn.Query(sql, new { NAME = Name });
Just to digress on Sam's answer, here is how I created two helper methods to make searches a bit easier using the LIKE operator.
First, creating a method for generating a parameterized query, this method uses dynamic: , but creating a strongly typed generic method should be more desired in many cases where you want static typing instead of dynamic.
public static dynamic ParameterizedQuery(this IDbConnection connection, string sql, Dictionary<string, object> parametersDictionary)
{
if (string.IsNullOrEmpty(sql))
{
return null;
}
string missingParameters = string.Empty;
foreach (var item in parametersDictionary)
{
if (!sql.Contains(item.Key))
{
missingParameters += $"Missing parameter: {item.Key}";
}
}
if (!string.IsNullOrEmpty(missingParameters))
{
throw new ArgumentException($"Parameterized query failed. {missingParameters}");
}
var parameters = new DynamicParameters(parametersDictionary);
return connection.Query(sql, parameters);
}
Then adding a method to create a Like search term that will work with Dapper.
public static string Like(string searchTerm)
{
if (string.IsNullOrEmpty(searchTerm))
{
return null;
}
Func<string, string> encodeForLike = searchTerm => searchTerm.Replace("[", "[[]").Replace("%", "[%]");
return $"%{encodeForLike(searchTerm)}%";
}
Example usage:
var sql = $"select * from products where ProductName like #ProdName";
var herringsInNorthwindDb = connection.ParameterizedQuery(sql, new Dictionary<string, object> { { "#ProdName", Like("sild") } });
foreach (var herring in herringsInNorthwindDb)
{
Console.WriteLine($"{herring.ProductName}");
}
And we get our sample data from Northwind DB:
I like this approach, since we get helper extension methods to do repetitive work.
My solution simple to this problem :
parameter.Add("#nomeCliente", dfNomeCliPesquisa.Text.ToUpper());
query = "SELECT * FROM cadastrocliente WHERE upper(nome) LIKE " + "'%" + dfNomeCliPesquisa.Text.ToUpper() + "%'";