Spring Integration - Externalizing JDBC Queries - java

Is there a simple way to externalize big sql queries from jdbc outbound gateways, instead of inlining it? The reason being that we're having to many big queries to make, and we'd like to have them on their own files, or at least externalize them in beans.
Some caveats:
I don't have control over the database, so I can't create anything
there (e.g. stored procedures)
I don't want to create classes just for this matter, I just want to organize/refactor it a bit, and not make it more complex
introducing many other steps
I'd prefer to create bare .sql files, but putting the queries in an xml withing a bean is okay too
I don't have the option of using hibernate, stuck to spring integration jdbc
Suggestions on how to better organize this, considering that we're going to have many others outbound gateways are welcome :)
For instance, I wouldn't like to have the SQL inline in the "int-jdbc:outbound-gateway" element as follows:
<int-jdbc:outbound-gateway
data-source="datasource"
request-channel="reqChannel"
reply-channel="respChannel"
row-mapper="datamapper" max-rows-per-poll="1000"
query=" SELECT Field1, Field2, ManyOthers
FROM Table T
JOIN A ON A.id = T.id [... many other joins here ...]
WHERE SOMECONDITION=:payload">
</int-jdbc:outbound-gateway>
What I've done using the answers
Simply:
<bean id="myCoolQuery" class="java.lang.String">
<constructor-arg>
<value>
<![CDATA[
SELECT Field1, Field2, ManyOthers
FROM Table T
JOIN A ON A.id = T.id [... many other joins here ...]
WHERE SOMECONDITION=:payload
]]>
</value>
</constructor-arg>
</bean>
<int-jdbc:outbound-gateway
data-source="datasource"
request-channel="reqChannel"
reply-channel="respChannel"
row-mapper="datamapper" max-rows-per-poll="1000"
query="#{myCoolQuery}">
</int-jdbc:outbound-gateway>
It also works with the ":payload" parameter used inside the bean.

Yes, you can put them in a properties file, and use properties placeholders ${...} to resolve them, or you can use SpEL...
"#{myQueryBean.queryOne}"
where myQueryBean is a <bean/> that's an instance of a class with a method...
public String getQueryOne() {...}
or a static constant on a class...
"#{T(foo.Queries).QUERY_ONE}"
public static final String QUERY_ONE = "...";

You can define your queries in XML as spring beans:
<bean id="exampleQuerySql" class="java.lang.String">
<constructor-arg>
<value>
<![CDATA[
select * from foo
where whatever_ind = 'A'
]]>
</value>
</constructor-arg>
</bean>
Using CDATA the query text can include newlines, angle brackets, etc., so it's legible and you can cut and paste it directly into a SQL tool.
You can refer to the bean using SpEL.

Related

Wildfly/Hibernate query result fails to return persisted entity

With Wildfly 16, using the EntityManager I persist a new entity and invoke another routine to perform an HQL query to retrieve that entity, but it fails to return.
This is running server side and is normally triggered by a client command. The command should cause the new object to be persisted, then call a routine used in several places that selects objects (like and including the newly persisted one) and format and push them to subscribers.
I've tried a few things to get this working:
Flush the EntityManager prior to the HQL query
Reuse the EntityManager via the invoked routine (with and without flushing post persist())
The only way I've managed to get the desired results is to cause the initial client command to persist the entity, then perform a second client command to retrieve the entity persisted via the first command. This is not an issue with the HQL retrieving the data as it does work - it just doesn't work immediately after persisting the entity. It seems like either the data isn't persisted prior to the HQL query or the HQL query is looking at something cached (although I haven't specifically set anything like that, so it would have to be a default I'm unaware of).
An example of the three routines:
//Routine A - calls B & C
routineB();
routineC();
//Routine B
EntityManager em = emProvider.getEntityManager(); //pulls em from a stateless bean tagged #PersistenceContext
Blah blah = new Blah();
em.persist(blah);
em.flush();
//Routine C
EntityManager em = emProvider.getEntityManager();
List<Blah> blahs = em.createNamedQuery("retrieveBlah").getResultList();
//do some stuff with blahs... except it's missing blah from Routine A
My persistence.xml settings JIC that's relevant
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL94Dialect" />
<property name="hibernate.connection.useUnicode" value="true" />
<property name="hibernate.connection.characterEncoding" value="UTF-8" />
<property name="hibernate.connection.charSet" value="UTF-8" />
<property name="hibernate.id.new_generator_mappings" value="true" />
I need to be able to persist and retrieve the persisted via HQL query as the result of a single client command.
It turns out the post above was missing the key bit of information to resolve the issue. After posting this I continued testing and discovered the piece I overlooked, "routineB()" was marked #Asynchronous. So it was executing in parallel to routineC().

How to use predefined SQL functions on entity find?

With plain SQL queries, we can use all predefined SQL functions like avg(),sum() etc.. But with moqui entities, we can only get data from the database. There are no predefined functions.
Is there any other way to achieve this kind of requirements like get data(manipulated data- performed some run-time functions like avg(),sum() ) from database with entity-find?
In Moqui functions are used through a view-entity definition using the #function attribute on the alias element. The view-entity may have one or more member entities. Here is an example from mantle-usl with a single member-entity:
<view-entity entity-name="AssetQuantitySummary" package="mantle.product.asset">
<member-entity entity-alias="AST" entity-name="mantle.product.asset.Asset"/>
<alias-all entity-alias="AST"><exclude field="quantityOnHandTotal"/><exclude field="availableToPromiseTotal"/>
<exclude field="originalQuantity"/></alias-all>
<alias name="quantityOnHandTotal" entity-alias="AST" function="sum"/>
<alias name="availableToPromiseTotal" entity-alias="AST" function="sum"/>
<alias name="originalQuantity" entity-alias="AST" function="sum"/>
</view-entity>
There are many more complex view-entity definitions in mantle-usl that you can use in your code, or that you can use as examples. These include more complex queries with sub-selects, nested functions, etc.

How fix control Query Persistence QL(hql)

I have issue with Hibernate query, my IDEA inspection error syntax:
This inspection controls whether the Persistence QL Queries are
error-checked
But I create mapping for Task objects in my hibernate.cfg.xml:
<session-factory>
<property name="connection.url">jdbc:postgresql://localhost:5432/todo_list</property>
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="connection.username">postgres</property>
<property name="connection.password">1</property>
<property name="dialect">org.hibernate.dialect.PostgreSQL95Dialect</property>
<mapping resource="ru/pravvich/model/Task.hbm.xml" />
</session-factory>
Facets:
If I cheating IDE and instead createQuery("select t from Task t"), create variable and push in createQuery
String hql = format("select t from Task t where t.id > %s", 0);
session.createQuery(hql)
It's work, but it's not normal code. How to fix this issue
Here is what for me resolve the same issue:
Open in the IDEA Preferences (Settings)/Editor/Language Injections and under the list of languages find Session (org.hibernate).
Under the column Language, it should be selected Hibernate QL.
Double click on it and a list of operations will be displayed.
Select the operations that you need.
IDEA doesn't recognise which or what Descriptor you are using. Check Project Structure -> Facets -> Hibernate. You should have found a cfg.xml file in Descriptors. If you are using package scanning through spring session factory definition,you should have found a session factory bean. If neither of them exists,you may add one.

Save sql query into external file

i use Spring for DB operation. For now i save my sql query into a xml file, like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>This query returns something extremely useful.
</comment>
<entry key="date.sql">
"update something set time = ? where sharedkey = ?"
</entry>
</properties>
And in the JDBCTemplate i have this:
public Properties readProperties(String xmlFileName) throws Exception {
Properties properties = new Properties();
InputStream is = this.getClass().getClassLoader().getResourceAsStream(xmlFileName);
properties.loadFromXML(is);
return properties;
}
public void updateDate(){
properties = readProperties("queries.xml");
sqlQuery = properties.getProperty("date.sql");
jdbcTemplateObject.update(sqlQuery,new Object[] {time,token});
}
Work but i think is not the best solution, how i can that with Spring? or there is other library for that?
You can achieve this in multiple ways. the best way to follow is
1> By Placing the Queries in a Property File and call them using Spring PropertyPlaceHolder
2> You can Still Use XML and can Get the Query by using JAXB or SAX XML Parser.
See the ElSql library which provides this functionality.
The small ElSql project allows an external file of SQL statements to be easily loaded and managed. It was originally written to work with Spring, and still does (although it now works without Spring as a dependency).
The file may be stored in "raw" SQL, or using a DSL:
-- an example comment
#NAME(SelectBlogs)
#PAGING(:paging_offset,:paging_fetch)
SELECT #INCLUDE(CommonFields)
FROM blogs
WHERE id = :id
#AND(:date)
date > :date
#AND(:active)
active = :active
ORDER BY title, author
#NAME(CommonFields)
title, author, content
// Java code:
bundle.getSql("SelectBlogs", searchArgs);
The DSL breaks the file into #NAME blocks that can be referred to from code. Each block is defined by significant whitespace indentation.
Additional tags can be used, such as #PAGING (that inserts the necessary code for paging such as FETCH/OFFSET) and #AND (that only outputs if the specified variable exists, for dynamically building searches). The goal of the optional DSL tags is to provide the common basics that often hit when trying to build dynamic SQL in a database-neutral way.
We use it for our SQL and avoid the significant overhead of an ORM.

How to map a Map<Calendar,Boolean> with jpa/hibernate

i've got 2 tables:
Seat(roomID,seatID,...)
SeatState(roomID,seatID,date,state)
i wanna create a seat class and i would like this class to have a Map attribute. does somebody know how to map this thing?
Hibernate supports using 'Map's as collections. If you are using hbm.xml files, you can use the <map> tag for this purpose. Take a look at http://docs.jboss.org/hibernate/core/3.3/reference/en/html/collections.html for a reference. Also Hibernate supports mapping Calendar fields to TIMESTAMP fields.
So if you are using XML, it should be something like
<map name="booking" table="BOOKING">
<key column="BOOKING_ID"/>
<map-key column="BOOKING_DATE" type="calendar"/>
<element column="IS_BOOKED" type="boolean"/>
</map>
Likewise, JPA / Hibernate Annotation alternatives also exist. You need to use #Embeddable to get this done. See http://hwellmann.blogspot.com/2010/07/jpa-20-mapping-map.html for an example.

Categories