How to join on foreign key in querydsl - java

I am new to querydsl and I'm trying to use querydsl in pure java (no hibernate, JPA or anything).
We have a database where the tables are linked through minimum 3 columns
I followed the doc here and ended up with my schema duly created.
Here are my pseudo tables :
Item
Corporation (pk) mmcono
Item number (pk) mmitno
Environnement (pk) mmenv
Item description mmitds
Item_warehouse
Corporation (fk for Item) mbcono
Item number (fk for Item) mbitno
Environnement (fk for Item) mbenv
Warehouse number mbwhlo
Other properties (not important)
Inside the Item_wharehouse class, I manually added the foreignKey (because it's not defined in the actual db schema)
public final com.querydsl.sql.ForeignKey<QItemWharehouse > _ItemWharehouseFk = createInvForeignKey(Arrays.asList(mbitno, mbcono, mbenv), Arrays.asList("mmitno", "mmcono", "mbenv"));
I'm working on the following code in my main class:
SQLTemplates templates = SQLServer2012Templates.builder().printSchema().build();
Configuration configuration = new Configuration(templates);
QItem mm = new QItem ("mm");
QItemWarehouse mb = new QItemWarehouse("mb");
JtdsDataSource ds = getDataSource();
SQLQueryFactory queryFactory = new SQLQueryFactory(configuration, ds);
String toto = queryFactory.select(mm.mmitno, mm.mmitds)
.from(mm)
.join( ???????????? )
.where(mb.mbwhlo.eq("122"))
.fetch()
As per doc here I should be able to do something like this : AbstractSQLQuery.innerJoin(ForeignKey<E> key, RelationalPath<E> entity)
What I want in the end is to allow joining table without having to specify manually all the columns required for the join condition.
As stated before, my model starts with minimum 3 columns in the pk, and it's not uncommon to have 6 or 7 cols in the on clause! It's a lot of typing and very error prone, because you can easily miss one and get duplicate results.
I would like something like .join(mb._ItemWharehouseFk, ???) and let querydsl handle little details like generating the on clause for me.
My trouble is that I can't find the second parameter of type RelationalPath<E> entity for the join method.
I am doing something wrong ? What do I miss ? Is it even possible to accomplish what I want ?

Oups I found the problem : I had it all in the wrong order.
The foreign key was located in the itemWarehouse class.
it should have been named this way :
public final com.querydsl.sql.ForeignKey<QItem> _ItemFk = createInvForeignKey(Arrays.asList(mbitno, mbcono, mbenv), Arrays.asList("mmitno", "mmcono", "mbenv"));
that means that you just have to reverse the order in the statement this way :
SQLTemplates templates = SQLServer2012Templates.builder().printSchema().build();
Configuration configuration = new Configuration(templates);
QItem mm = new QItem ("mm");
QItemWarehouse mb = new QItemWarehouse("mb");
JtdsDataSource ds = getDataSource();
SQLQueryFactory queryFactory = new SQLQueryFactory(configuration, ds);
List<Tuple> toto = queryFactory.select(mm.mmitno, mm.mmitds)
.from(mb)
.join(mb._ItemFk, mm )
.where(mb.mbwhlo.eq("122"))
.fetch()
And you get your nice on clause generated. It's just a question of how you construct your relation.
#Enigma, I sincerely hope it will help you for your Friday afternoon. I wouldn't want your boss to be disappointed with you :-)

Related

"No explicit selection and an implicit one could not be determined" error for my JPA request

I have three SQL tables
The "Accedant" table that has an idAccedant
The "Emetteur" table that has an idEmetteur and a foreign key towards the Accedant table
The "Asso_Etab_Emet" table which is an assocation table between a table called "Etablissement" and the "Emetteur" table.
So the "Asso_Etab_Emet" table has two foreign keys, one towards the Etablissement table and one toward the Emetteur table
I want to build a
Specification<Etablissement>
with the idAccedant as input data, to get, at the end, the list of all the Etablissement linked to an Emetteur :
Page<Etablissement> etablissementsPage = etablissementDao.findAll(specEtab, configPage);
My code that aims at building the Specification is the following
#Override
public Specification<Etablissement> findActifByExploitantSpec(Long idAccedant) {
return ((root, query, builder) -> {
Subquery<Emetteur> emetteurSubquery = query.subquery(Emetteur.class);
Root<Emetteur> emetteurRoot = emetteurSubquery.from(Emetteur.class);
emetteurSubquery
= emetteurSubquery.where(builder.equal(emetteurRoot.get(Emetteur_.accedant).get(Accedant_.id), idAccedant));
Subquery<Etablissement> etablissementSubquery = query.subquery(Etablissement.class);
Root<Etablissement> etablissementRoot = etablissementSubquery.from(Etablissement.class);
Root<EtablissementEmetteur> etablissementEmetteurRoot = etablissementSubquery.from(EtablissementEmetteur.class);
etablissementSubquery
= etablissementSubquery.select(etablissementEmetteurRoot.get(EtablissementEmetteur_.etablissement));
etablissementSubquery = etablissementSubquery.where(
builder.in(etablissementEmetteurRoot.get(EtablissementEmetteur_.emetteur)).value(emetteurSubquery));
return builder.in(root).value(etablissementSubquery);
});
}
I have a
"No explicit selection and an implicit one could not be determined" error
when testing via my Angular webapp

Select with a set of predefined values in JOOQ for PostgreSQL

Does anybody know is it possible to write in JOOQ select with a set of predefined values? I need it for insert if not exists.
For example,
INSERT INTO test
(text)
SELECT '1234567890123456789'
WHERE
NOT EXISTS (
SELECT id FROM test WHERE text = '1234567890123456789'
);
I've found the answer by myself:
List<Param<?>> params = new LinkedList<>();
params.add(DSL.val("1234567890123456789"));
List<Field<?>> fields = new LinkedList<>();
fields.add(TEST.TEXT);
SelectConditionStep<Record1<TEXT>> notExistsSelect = context.select(TEST.TEXT).from(TEST).where(TEST.TEXT.eq("1234567890123456789"));
SelectConditionStep<Record> insertIntoSelect = context.select(params).whereNotExists(notExistsSelect);
context.insertInto(TEST, fields).select(insertIntoSelect).execute();
But it would be great, if we had an ability to do it via InsertQuery. I've not found the way to do it.

How to get fully materialized query from querydsl

I am trying to use querydsl for building dynamic queries for dynamic schemas. I am trying to get just the query instead of having to actually execute it.
So far I have faced two issues:
- The schema.table notation is absent. Instead I only get the table name.
- I have been able to get the query but it separates out the variables and puts '?' instead which is understandable. But I am wondering if there is some way to get fully materialized query including the parameters.
Here is my current attempt and result(I am using MySQLTemplates to create the configuration):
private SQLTemplates templates = new MySQLTemplates();
private Configuration configuration = new Configuration(templates);
String table = "sometable"
Path<Object> userPath = new PathImpl<Object>(Object.class, table);
StringPath usernamePath = Expressions.stringPath(userPath, "username");
NumberPath<Long> idPath = Expressions.numberPath(Long.class, userPath, "id");
SQLQuery sqlQuery = new SQLQuery(connection, configuration)
.from(userPath).where(idPath.eq(1l)).limit(10);
String query = sqlQuery.getSQL(usernamePath).getSQL();
return query;
And what I get is:
select sometable.username
from sometable
where sometable.id = ?
limit ?
What I wanted to get was:
select sometable.username
from someschema.sometable
where sometable.id = ?
limit ?
Update: I came up with this sort of hack to get parameters materialized(Not ideal and would love better solution) But still could not get Schema.Table notation to work:
Hack follows. Please suggest cleaner QueryDsl way of doing it:
String query = cleanQuery(sqlQuery.getSQL(usernamePath));
private String cleanQuery(SQLBindings bindings){
String query = bindings.getSQL();
for (Object binding : bindings.getBindings()) {
query = query.replaceFirst("\\?", binding.toString());
}
return query;
}
To enable schema printing use the following pattern
SQLTemplates templates = MySQLTemplates.builder()
.printSchema()
.build();
SQLTemplates subclasses were used before, but since some time the builder pattern is the official way to customize the templates http://www.querydsl.com/static/querydsl/3.3.1/reference/html/ch02s03.html#d0e904
And to enable direct serialization of literals use
//configuration level
configuration.setUseLiterals(true);
//query level
configuration.setUseLiterals(true);
Here is a full example
// configuration
SQLTemplates templates = MySQLTemplates.builder()
.printSchema()
.build();
Configuration configuration = new Configuration(templates);
// querying
SQLQuery sqlQuery = new SQLQuery(connection, configuration)
.from(userPath).where(idPath.eq(1l)).limit(10);
sqlQuery.setUseLiterals(true);
String query = sqlQuery.getSQL(usernamePath).getSQL();
If you always just want the SQL query string out, move setUseLiterals from query to configuration.
Concerning the usage of Querydsl expressions the usage of code generation like documented here is advised http://www.querydsl.com/static/querydsl/3.3.1/reference/html/ch02s03.html
It will make your code typesafe, compact and readable.
If you want to try Querydsl without code generation you can replace
Path<Object> userPath = new PathImpl<Object>(Object.class, variable);
with
Path<Object> userPath = new RelationalPathBase<Object>(Object.class, variable, schema, table);
When working with QueryDSL, you must provide a template for the database platform to build the query for. I see you are already are doing this here:
private SQLTemplates templates = new MySQLTemplates();
private Configuration configuration = new Configuration(templates);
To make the schema name appear in the generated query, the only way I have found to do this is (there may be an easier way) is to extend the template class and explicitly call this.setPrintSchema(true); inside the constructor. Here is a class that should work for MySql:
import com.mysema.query.sql.MySQLTemplates;
public class NewMySqlTemplates extends MySQLTemplates {
public NewMySqlTemplates() {
super('\\', false);
}
public NewMySqlTemplates(boolean quote) {
super('\\', quote);
}
public NewMySqlTemplates(char escape, boolean quote) {
super(escape, quote);
this.setPrintSchema(true);
}
}
Then simply use this NewMySqlTemplates class in place of the MySQLTemplates class like this:
private SQLTemplates templates = new NewMySQLTemplates();
private Configuration configuration = new Configuration(templates);
I have this working using PostgresTemplates, so I may have a typo or mistake in the NewMySqlTemplates class above, but you should be able to get it to work. Good luck!

ORMLite Android- getting 1 column out of 17 from single table

I have one table "company" with contains 3 columns [ name,id ,address ]. But I want to fetch only "name " column values. I am doing like this
Dao<Company, String> dao = getDBHelper().getCompanyDao();
QueryBuilder<Company, String> queryBuilder = dao.queryBuilder();
queryBuilder.orderBy(Company.USERNAME_FIELD_NAME, true);
**queryBuilder.selectColumns("name");**
PreparedQuery<Company> preparedQuery = queryBuilder.prepare();
CloseableIterator<Company> iterator = dao.iterator(preparedQuery);
AndroidDatabaseResults results = (AndroidDatabaseResults) iterator.getRawResults();
Cursor cursor = results.getRawCursor();
But application is crashing when I add "queryBuilder.selectColumns("name") ". Please help me.I want cursor since am using cursoradapter to fill data.
crash logs are here :
java.lang.NullPointerException
at com.j256.ormlite.stmt.QueryBuilder.appendFieldColumnName(QueryBuilder.java:593)
at com.j256.ormlite.stmt.QueryBuilder.appendColumns(QueryBuilder.java:585)
at com.j256.ormlite.stmt.QueryBuilder.appendStatementStart(QueryBuilder.java:405)
at com.j256.ormlite.stmt.StatementBuilder.appendStatementString(StatementBuilder.java:122)
at com.j256.ormlite.stmt.StatementBuilder.buildStatementString(StatementBuilder.java:106)
at com.j256.ormlite.stmt.StatementBuilder.prepareStatement(StatementBuilder.java:74)
at com.j256.ormlite.stmt.QueryBuilder.prepare(QueryBuilder.java:98)
But application is crashing when I add "queryBuilder.selectColumns("name")
Wow. This is a very subtle and long standing bug. It will happen whenever you try to select columns from an entity that does not have an ID field. I've added this bug report:
https://sourceforge.net/p/ormlite/bugs/162/
This has been fixed in trunk and will be in 4.49. Here's the check-in.
https://github.com/j256/ormlite-core/commit/ee95883040a3618a1d455b44f6bfd6935a8d4ec7

jOOQ insert query with returning generated keys

I installed jOOQ into eclipse, generated classes for my mySQL, but I still have problems to write also some basic queries.
I tried to compose insert query with returning of generated keys, but compiler throws error
Table: tblCategory
Columns: category_id, parent_id, name, rem, uipos
Result<TblcategoryRecord> result= create.insertInto(Tblcategory.TBLCATEGORY,
Tblcategory.PARENT_ID, Tblcategory.NAME, Tblcategory.REM, Tblcategory.UIPOS)
.values(node.getParentid())
.values(node.getName())
.values(node.getRem())
.values(node.getUipos())
.returning(Tblcategory.CATEGORY_ID)
.fetch();
tried also other differnt ways
how to do it right way?
thanks
charis
The syntax you're using is for inserting multiple records. This is going to insert 4 records, each with one field.
.values(node.getParentid())
.values(node.getName())
.values(node.getRem())
.values(node.getUipos())
But you declared 4 fields, so that's not going to work:
create.insertInto(Tblcategory.TBLCATEGORY,
Tblcategory.PARENT_ID, Tblcategory.NAME, Tblcategory.REM, Tblcategory.UIPOS)
What you probably want to do is this:
Result<TblcategoryRecord> result = create
.insertInto(Tblcategory.TBLCATEGORY,
Tblcategory.PARENT_ID, Tblcategory.NAME, Tblcategory.REM, Tblcategory.UIPOS)
.values(node.getParentid(), node.getName(), node.getRem(), node.getUipos())
.returning(Tblcategory.CATEGORY_ID)
.fetch();
Or alternatively:
Result<TblcategoryRecord> result = create
.insertInto(Tblcategory.TBLCATEGORY)
.set(Tblcategory.PARENT_ID, node.getParentid())
.set(Tblcategory.NAME, node.getName())
.set(Tblcategory.REM, node.getRem())
.set(Tblcategory.UIPOS, node.getUipos())
.returning(Tblcategory.CATEGORY_ID)
.fetch();
Probably, you're even better off by using
TblcategoryRecord result =
// [...]
.fetchOne();
For more details, consider the manual:
http://www.jooq.org/doc/2.6/manual/sql-building/sql-statements/insert-statement/
Or the Javadoc for creating INSERT statements that return values:
http://www.jooq.org/javadoc/latest/org/jooq/InsertReturningStep.html
preffered SOLUTION
try {
TblcategoryRecord record = (TblcategoryRecord) create
.insertInto(Tblcategory.TBLCATEGORY)
.set(Tblcategory.PARENT_ID, node.getParentid())
.set(Tblcategory.NAME, node.getName())
.set(Tblcategory.REM, node.getRem())
.set(Tblcategory.UIPOS, node.getUipos())
.returning(Tblcategory.CATEGORY_ID)
.fetchOne();
node.setId(record.getCategoryId());
} catch (SQLException e1) { }
Try
YoutableRecord result = create
.insertInto(YOURTABLE)
.set(YOURTABLE.PROD_NAME, "VAL")
.returning(YOURTABLE.ID_PR)
.fetchOne();
int id = result.getValue(Products.PRODUCTS.ID_PR);

Categories

Resources