Automatically generated database requests - java

How do you implement automatically generated database (let it be SQL) requests?
Let us have offline shop with filters:
The database is standalone offline.
SO if I want to filter items by Price the request would be something like:
select Snowboard.Name
from Snowboard
where Snowboard.Price between 400 and 600;
And if I filter by two characteristics e.g. Price from and Camber. There would be:
select s.Name, s.Camber
from Snowboard s
where s.Price between 400 and 600
and s.Camber in ('Rocker', 'Hybrid');
The question is how could it be implemented in Java so that these requests are generated automatically from any combination of filters selected?

Quick and dirty solution #1
Generate a query at run time & make clever use of WHERE 1=1 condition as the number of where clause are unknown. (This sample is in C# but works more or less the same with JAVA as well)
string sql= #"select Snowboard.Name
from Snowboard
where 1=1";
Now you can build your query based on the UI element selections like
string whereClause="";
if(yourCheckBoxPrice.Checked)
{
whereClause+= " AND Price BETWEEN "+ txtPriceFrom.Text + " AND "+ txtPriceTo.Text;
}
if(yourCheckBoxCamber.Checked)
{
whereClause+= " AND Camber IN ("+ /* your list of values go here */ +")";
}
sql += whereClause;
2nd Solution (Use SQL CASE)
You can use SQL CASE inside your query for each where clause to check for nulls or specific values. But beware, dynamic SQL will make your code pretty messy & hard to read (Can be done via a stored procedure as well)
SQL- CASE Statement
I advise you to use a stored procedure with a mix of both options 1 and 2. Implementing Dynamic SQL Where Clause. Keep it simple and you are good to go.

Related

Android Room Paging Results For Export. Potential Problems with my solution

I'm writing an android app that supports exporting the app database to various formats. I don't want to run out of memory, but I want to page the results easily without receiving updates when it changes. So I put it in a service, and came up with the following method of paging.
I use a limit clause in my query to limit the number of results returned and I'm sorting on the primary key. So it should be fast. I use a set of nested for loops to execute the series of queries until no results are returned, and walk through the given results, so that's linear. It's in a service, so it doesn't matter that I'm using immediate result things here.
I feel like I might be doing something bad here. Am I?
// page through all results
for (List<CountedEventType> typeEvents = dao.getEventTypesPaged2(0);
typeEvents.size() > 0;
typeEvents = dao.getEventTypesPaged2(typeEvents.get(typeEvents.size() - 1).uid)
) {
for (CountedEventType type : typeEvents) {
// Do something for every result.
}
}
Here's my dao method.
#Dao
interface ExportDao {
#Query("SELECT * FROM CountedEventType WHERE uid > :lastUid ORDER BY uid ASC LIMIT 4")
List<CountedEventType> getEventTypesPaged2(int lastUid);
}

How to add multiline Strings in Java?

How to make long queries more readable?
For example I have this one:
String query = "SELECT CASE WHEN EXISTS (SELECT * FROM users WHERE username = 'username' AND user_password = crypt('password', user_password)) THEN 'match' ELSE 'differ' END";
And it's completely unreadable, are there any ways to beautify it?
Since Java 15, you can use text blocks:
String query = """
SELECT CASE
WHEN
EXISTS (
SELECT *
FROM users
WHERE
username = 'username'
AND user_password = crypt('password', user_password)
)
THEN 'match'
ELSE 'differ'
END
""";
In cases when you don't wont to blend SQL and JAVA you can put SQL queries in an .sql file. And get this text when needed.
public class QueryUtil {
static public String getQuery(String fileName) throws IOException {
Path path = Paths.get("src/test/resources//" + fileName + ".sql");
return Files.readAllLines(path).get(0);
}
}
If you can mix SQL and JAVA then starting from JDK15 you can use text blocks for this.
Also you can generates Java code from your database by using JOOQ, it gives many benefits.
Assuming that you can't move to a newer-than-8 version of Java (or even if you can), by far the best solution is to use an ORM. For Java it pretty much comes down to Hibernate, or jOOQ. jOOQ (and possibly Hibernate, I haven't used it so can't say, sorry) allows you to use a fluent programming interface, which is very much in keeping with existing Java code style and patterns.
Another specific advantage of using an ORM is that you can very easily change which DB engine you use without having to change the Java code that you've written beyond changing the SQL dialect in your setup functions. See https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/SQLDialect.html.
You can use JOOQ and get multiple other benefits like type safety, auto-complete, easy mapping and great support.
Have used it for several projects so far and also competition like Kotlin Exposed but always came back to JOOQ.
Move to Java 13+. There are Text Blocks for this.
Or use some ORM library.

Save the result set of a query against a view to a table using the java client library

Recently, one of our clients reported not being able to create a table based on a query against a view. That said, they were able to save the result of a query against a table into another table. This issue spawned a more implementation focused question using the Java client libraries. Specifically, is there any way to save the result set of a query against a view to a table using the Java client library? I will be digging and post anything that I find. That said, any early guidance would be appreciated!
To be specific and add more context, I note that the the following process failed when the query was run against a union view.
java -jar BigQueryToCloudExporter.jar ./GAFastAccessKey.p12 '' "
Select date(date_add('2014-08-09',floor(datediff(date(sec_to_timestamp(visitstarttime)),'2014-08-03')/7)*7,"DAY")) WeekEndDate
, hits.eventinfo.eventaction GA_RentalNo
, count(distinct visitID) PDP_PPC
FROM (TABLE_DATE_RANGE([Union_View.GA],
TIMESTAMP('2014-08-30'),
TIMESTAMP('2014-09-13')))
where hits.eventinfo.eventcategory='property attributes'
and brandId=121
--hits.eventinfo.eventcategory='property inquiry'
and trafficsource.medium like '%cpc%'
--and trafficsource.campaign not like '%ppb%'
and trafficsource.campaign like '%mpm%'
group each by WeekEndDate, GA_XXXXXX
order by WeekEndDate, GA_XXXXXX limit 100" StagingQueryTable QueryTable AVRO gs://XXXXXX/QueryTable*.avro
On the other hand, the following process succeeded when the query was made against a BigQuery table (keeping everything else same).
java -jar BigQueryToCloudExporter.jar ./GAFastAccessKey.p12 '' "
Select date(date_add('2014-08-09',floor(datediff(date(sec_to_timestamp(visitstarttime)),'2014-08-03')/7)*7,"DAY")) WeekEndDate
, hits.eventinfo.eventaction GA_XXXXXX
, count(distinct visitID) PDP_PPC
FROM (TABLE_DATE_RANGE([XXXXXX.ga_sessions_],
TIMESTAMP('2014-08-30'),
TIMESTAMP('2014-09-13')))
where hits.eventinfo.eventcategory='property attributes'
and brandId=121
--hits.eventinfo.eventcategory='property inquiry'
and trafficsource.medium like '%cpc%'
--and trafficsource.campaign not like '%ppb%'
and trafficsource.campaign like '%mpm%'
group each by WeekEndDate, GA_RentalNo
order by WeekEndDate, GA_XXXXXX limit 100" StagingQueryTable QueryTable AVRO gs://XXXXXX/QueryTable*.avro

MySQL enum datatype access in clojure

I am trying to write a simple application which reads the a database and produces a set of functions with which to access it; so far so good. Now, what I have come across is that some of the columns in my database are defined as MySQL enum types (e.g. ENUM('red','green','violet')) and I would like to validate the stuff I send to the database rather than receive an error from the driver when an unacceptable value is given, so I was wondering if there is a way to retrieve the possible values for the enum from within clojure.
I am using [clojure.java.jdbc "0.3.0-alpha5"] and [mysql/mysql-connector-java "5.1.25"]. In order to get the metadata for the table I am currently using java.sql.DatabaseMetaData, but trying .getPseudoColumns just gives me nil every time.
Turns out there is no straight forward way to do this using libraries. My own solution is:
(defn- parse-enum
"Parses an enum string and returns it's components"
[enum-str]
; "enum('temp','active','canceled','deleted')"
(map (comp keyword #(.replace % "'" ""))
(-> enum-str
(.replaceFirst "^[^\\(]+\\(([^\\)]+)\\)$" "$1")
(.split "'?,'?"))))
(defn get-enum-value
"Returns the values for an enum in a table.column"
[table column]
(jdbc/with-connection db
(jdbc/with-query-results rs
[(str "show columns from " table " where field = ?") column]
((comp set parse-enum :type first) rs))))

Problem with JDOQL to obtain results with a "contains" request

I am using Google App Engine for a project and I need to do some queries on the database. I use the JDOQL to ask the database. In my case I want to obtain the university that contains the substring "array". I think my query has a mistake because it returns the name of universities in the alphabetical order and not the ones containing the substring.
Query query = pm.newQuery("SELECT FROM " + University.class.getName() + " WHERE name.contains("+array+") ORDER BY name RANGE 0, 5");
Could someone tell me what's wrong in my query?
Thank you for your help!
EDIT
I have a list of universities store and I have a suggestbox where we can request a university by his name. And I want to autocomplete the requested name.
App engine does not support full-text searches, you should star issue 217. However, A partial workaround is possible. And in your case I think it is a good fit.
First thing, adjust your model such that there is a lower (or upper case) version of the name as well -- I will assume it is called lname. Unless you want your queries to be case-sensitive.
Then you query like this:
Query query = pm.newQuery(University.class);
query.setFilter("lname >= startNameParam");
query.setFilter("lname < stopNameParam");
query.setOrdering("lname asc");
query.declareParameters("String startNameParam");
query.declareParameters("String stopNameParam");
query.setRange(0, 5);
List<University> results = (List<University>) query.execute(search_value, search_value + "z");
The correct way to do this is like this -
Query query = pm.newQuery(University.class,":p.contains(name)");
query.setOrdering("name asc");
query.setRange(0, 5);
List univs = q.execute(Arrays.asList(array));
(note- In this case the :p is an implicit param name you can replace with any name)

Categories

Resources