JoinClause - Invalid statement. Please check aliases, field identifiers, projections and query conditions - java

I'm using Dynamicloud and something is wrong in my code:
This is my code:
RecordModel contactFormModel = new RecordModel(contactModelId);
RecordModel userFormModel = new RecordModel(userContactModelId);
DynamicProvider<ContactForm> provider = new DynamicProviderImpl<ContactForm>(new RecordCredential(csk, aci));
contactFormModel.setBoundClass(ContactForm.class);
Query<ContactForm> query = provider.createQuery(contactFormModel);
query.setAlias("contact");
query.setProjection("contact.namef, contact.comments");
JoinClause join = Conditions.innerJoin(userFormModel, "user", "contact.id = user.contactid");
query.join(join);
try {
RecordResults<ContactForm> list = query.add(Conditions.like("contact.namef", "ProBusiness%")).list();
System.out.println("list = " + list.getFastReturnedSize());
if (list.getFastReturnedSize() > 0) {
System.out.println("Contact Name = " + list.getRecords().get(0).getName());
System.out.println("Contact Comments = " + list.getRecords().get(0).getComments());
}
} catch (DynamicloudProviderException e) {
e.printStackTrace();
}
This code throws the following Exception:
org.dynamicloud.exception.DynamicloudProviderException: Invalid statement. Please check aliases, field identifiers, projections and query conditions.
What's wrong?
Thanks!

Hi
The problems is the missing "id" in your projection section. You need to set the id of one model involved in your JoinClause.
Dynamicloud always order the results by Id, in this case the query has ambiguous columns user.id and contact.id"
Try to put the following id in your projection (of course depends of your needs):
query.setProjection("contact.id, contact.namef, contact.comments");
Hope this helps!

Related

How to find the selected id in my List<String> ids arraylist?

Here is my code. I am trying to use JUnit to test the deleteUsers() method, but everytime I write my test, it deletes all the users that I have in the database. How can i delete a single user? Below is the code for the method and for the test.
#Override
public boolean deleteUsers(List<String> ids) throws Exception {
StringBuilder sql = new StringBuilder();
sql.append("delete from user where ");
for (String id : ids) {
sql.append(" id = ? or");
}
String strSql = sql.toString().substring(0, sql.length() - 2) + ";";
PreparedStatement preparedStatement = this.connection.prepareStatement(strSql);
for (int i = 0; i < ids.size(); i++) {
preparedStatement.setInt(1 + i, Integer.parseInt(ids.get(i)));
}
int lines = preparedStatement.executeUpdate();
preparedStatement.close();
return lines > 0;
}
You're missing a check for empty input. In your test you pass an empty list to deleteUsers which results in this SQL statement:
delete from user wher;
I'd expect that the DBMS would reject this as invalid SQL but perhaps there are some where this is interpreted as delete from user which simply deletes all users. (As #SteveBosman pointed out the wher is interpreted as table alias as it is - due to the missing last e - no reserved word anymoere)
Basically you have 2 options. Either deleting all users by passing an empty list is a valid use case - in which case you should handle it properly by producing proper SQL. Or this is not expected and you should adapt your code to throw an Exception if ids is empty.
#Override
public boolean deleteUsers(List<String> ids) throws Exception {
if (ids == null || ids.size() == 0) {
throw new IllegalArgumentException("List of IDs must not be empty");
}
...
}
You could of course return false in case of an empty input as well to indicate no users were deleted.
To pass values to the deleteUsers method in your test you need to add values to the used list:
userDAOImpl.addUser("admin3", "111222");
final List<String> idsToDelete = new ArrayList<>();
idsToDelete.add("111222");
userDAOImpl.deleteUsers(idsToDelete);
The problem is caused by how the SQL is built. When deleteUsers is passed an empty list then the generated SQL will be:
delete from user wher
which will result in all data being deleted (the table user is given the alias "wher"). I highly recommend checking at the start of the method if the collection is empty and either raising an exception or returning.
Add the following check
if (ids == null || ids.isEmpty()) {
throw new IllegalArgumentException("ids must not be empty");
}
StringBuilder sql = new StringBuilder();
sql.append("delete from user where");
String orClause = "";
for (String id : ids) {
sql.append(orClause);
sql.append(" id = ?");
orClause = " or";
}

DQL drop group_name in DFC

I have a list of group names which the app is reading from external .txt.
I want to pass to method as a List <String> group names and to execute dql query something like:
for (String s : groupnames) {
dql = "DROP GROUP " + s;
System.out.println("dropped group: " + s;
}
How to write/execute DQL?
I have done it by myself:
private static void deleteGroups(List<String> groupsToDelete) {
try {
DfClientX clientX = new DfClientX();
IDfQuery query = clientX.getQuery();
for (String s : groupsToDelete){
query.setDQL("DROP GROUP '" + s + "'");
printInfo("Executing DQL: " + query.getDQL());
query.execute(_session, 0);
}
} catch (DfException e) {
printError(e.getMessage());
DfLogger.error("app", "DQL DROP GROUP execution", null,e);
}
}
Not quite sure does CS permit to delete group via DFC but it should be like:
IDfQuery query = new DfQuery();
query.setDQL("DROP GROUP <group_name>");
query.execute(getSession(), IDfQuery.DF_EXEC_QUERY);
There is for sure way to instantiate group object in memory and call .delete() method. I'll try to check it out.

Hibernate IN clause from a JSONArray

I am trying get a list with Hibernate. I get a JSONArray with the IN values.
JSONArray rolesEnSession is, for example, ["ROLE1", "ROLE2"]
public List<MenuLeft> getMenu(JSONArray rolesEnSession){
String listaRoles = "";
for (int i = 0; i < rolesEnSession.length(); i++) {
try {
listaRoles = listaRoles + "'"+rolesEnSession.getString(i)+",";
} catch (JSONException ex) {
Logger.getLogger(LinkDAOImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
return getCurrentSession()
.createQuery("from MenuLeft "
+ "WHERE menuRol in (:rolesEnSession)")
.setParameter("rolesEnSession", listaRoles)
.list();
}
But I get a blank value. The column menuRol in database has values ROLE1 and ROLE2
Consider listaRoles after two iterations with your current code. It will have value:
'ROLE_1,'ROLE_2,
Instead of trying to build a well-formed string of parameter values, just change listaRoles to be an actual List and pass that in as a parameter. Let Hibernate handle the conversion to a valid SQL.

Cassandra Sample Trigger Code to get inserted value

I need your help on extract column names and values in the trigger augment method.
Table Def :
create table dy_data (
id timeuuid,
data_key text,
time timestamp,
data text,primary key((id,data_key),time)
) with clustering order by (time desc);
Trigger Code :
public class ArchiveTrigger implements ITrigger {
public Collection<RowMutation> augment(ByteBuffer key, ColumnFamily cf) {
try {
// Below loop only has 2 columns ( one is "data" and another one may be "time" but i am not sure, because i cannot get value.
for (Column cell : cf) {
//Got Exception if I try to get column name
String name = ByteBufferUtil.string(cell.name());
//Got only "data" column value and empty value for another column may be "time". If I try ByteBufferUtil.toLong(cell.value()) it throws exception
String value = ByteBufferUtil.string(cell.value());
log(" name = " + name);
log(" value = " + value);
}
} catch (Exception e) {
logger.warn("Exception ", e);
}
return null;
}
}
I tried my best to search sample code in google. But failed. Please help me with sample code. Thanks in advance.
Thanks iamaleksey for you help. Your replies helps me a lot.
Below are the code snippet which will be useful for trigger users,
public Collection<RowMutation> augment(ByteBuffer key, ColumnFamily cf) {
try {
ByteBuffer id_bb = CompositeType.extractComponent(key, 0);
UUID id=TimeUUIDType.instance.compose(id_bb);
ByteBuffer data_key_bb = CompositeType.extractComponent(key, 1);
String data_key=UTF8Type.instance.compose(data_key_bb);
Iterator col_itr=cf.iterator();
Column ts_col=(Column)col_itr.next();
ByteBuffer time_bb=CompositeType.extractComponent(ts_col.name(),0);
long time=(TimestampType.instance.compose(time_bb)).getTime();
Column data_bb=(Column)col_itr.next();
String data=UTF8Type.instance.compose(data_bb.value());
log(" id --> "+id.toString());
log(" data_key-->"+data_key);
log(" time == "+time);
log(" data == "+data);
} catch (Exception e) {
logger.warn("Exception ", e);
}
return null;
}
PS: Since I know my table format, I hardcoded the column comparator type. If we want to write generic trigger code, we can use cf.getComponentComparator()/getKeyValidator().
To get 'id' and 'data_key' you'll have to split the key (ByteBuffer key, the first argument to augment). 'time' will be in the first part of cell.name() - you'd still need to split it. 'data' will be the cell.value(), no need to do any splitting.

Hibernate order by with nulls last

Hibernate used with PostgreSQL DB while ordering desc by a column puts null values higher than not null ones.
SQL99 standard offers keyword "NULLS LAST" to declare that null values should be put lower than not nulls.
Can "NULLS LAST" behaviour be achieved using Hibernate's Criteria API?
This feature has been implemented during Hibernate 4.2.x and 4.3.x releases as previously mentioned.
It can be used as for example:
Criteria criteria = ...;
criteria.addOrder( Order.desc( "name" ).nulls(NullPrecedence.FIRST) );
Hibernate v4.3 javadocs are less omissive here.
Given that HHH-465 is not fixed and is not going to get fixed in a near future for the reasons given by Steve Ebersole, your best option would be to use the CustomNullsFirstInterceptor attached to the issue either globally or specifically to alter the SQL statement.
I'm posting it below for the readers (credits to Emilio Dolce):
public class CustomNullsFirstInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = -3156853534261313031L;
private static final String ORDER_BY_TOKEN = "order by";
public String onPrepareStatement(String sql) {
int orderByStart = sql.toLowerCase().indexOf(ORDER_BY_TOKEN);
if (orderByStart == -1) {
return super.onPrepareStatement(sql);
}
orderByStart += ORDER_BY_TOKEN.length() + 1;
int orderByEnd = sql.indexOf(")", orderByStart);
if (orderByEnd == -1) {
orderByEnd = sql.indexOf(" UNION ", orderByStart);
if (orderByEnd == -1) {
orderByEnd = sql.length();
}
}
String orderByContent = sql.substring(orderByStart, orderByEnd);
String[] orderByNames = orderByContent.split("\\,");
for (int i=0; i<orderByNames.length; i++) {
if (orderByNames[i].trim().length() > 0) {
if (orderByNames[i].trim().toLowerCase().endsWith("desc")) {
orderByNames[i] += " NULLS LAST";
} else {
orderByNames[i] += " NULLS FIRST";
}
}
}
orderByContent = StringUtils.join(orderByNames, ",");
sql = sql.substring(0, orderByStart) + orderByContent + sql.substring(orderByEnd);
return super.onPrepareStatement(sql);
}
}
You can configure "nulls first" / "nulls last" in hibernate properties so it will be picked up by any criteria call by default: hibernate.order_by.default_null_ordering=last (or =first).
See this hibernate commit for details.
We can create Pageable object with following Sort parameter:
JpaSort.unsafe(Sort.Direction.ASC, "ISNULL(column_name), (column_name)")
We can prepare HQL as well:
String hql = "FROM EntityName e ORDER BY e.columnName NULLS LAST";
Here's my update to the class by (Pascal Thivent):
for (int i = 0; i < orderByNames.length; i++) {
if (orderByNames[i].trim().length() > 0) {
String orderName = orderByNames[i].trim().toLowerCase();
if (orderName.contains("desc")) {
orderByNames[i] = orderName.replace("desc", "desc NULLS LAST");
} else {
orderByNames[i] = orderName.replace("asc", "asc NULLS FIRST");
}
}
}
This fixes the problem:
This breaks if sql has limit/offset after order by – Sathish Apr 1 '11 at 14:52
Also here's how you can use this within JPA (hibernate):
Session session = entityManager.unwrap(Session.class);
Session nullsSortingProperlySession = null;
try {
// perform a query guaranteeing that nulls will sort last
nullsSortingProperlySession = session.getSessionFactory().withOptions()
.interceptor(new GuaranteeNullsFirstInterceptor())
.openSession();
} finally {
// release the session, or the db connections will spiral
try {
if (nullsSortingProperlySession != null) {
nullsSortingProperlySession.close();
}
} catch (Exception e) {
logger.error("Error closing session", e);
}
}
I've tested this on postgres and it fixes the 'nulls are higher than non-nulls' issue that we were having.
Another variant, if you create SQL on the fly and don't use Criteria API:
ORDER BY COALESCE(,'0') [ASC|DESC]
This works either for varchar or numeric columns.
For future travellers... I solved this by overriding the Hibernate dialect. I needed to add null first for asc and null last for desc by default in CriteriaQuery, which is for some reason not supported. (It's supported in legacy CriteriaAPI)
package io.tolgee.dialects.postgres
import org.hibernate.NullPrecedence
import org.hibernate.dialect.PostgreSQL10Dialect
#Suppress("unused")
class CustomPostgreSQLDialect : PostgreSQL10Dialect() {
override fun renderOrderByElement(expression: String?, collation: String?, order: String?, nulls: NullPrecedence?): String {
if (nulls == NullPrecedence.NONE) {
if (order == "asc") {
return super.renderOrderByElement(expression, collation, order, NullPrecedence.FIRST)
}
if (order == "desc") {
return super.renderOrderByElement(expression, collation, order, NullPrecedence.LAST)
}
}
return super.renderOrderByElement(expression, collation, order, nulls)
}
}
There appears to be a change request/bug ticket open for this

Categories