This is an hypothetic example; well, quite not hypothetic since I reproduced it in code:
public final class JooqPlayAround
{
public static void main(final String... args)
throws IOException, SQLException
{
final Path path = Paths.get("/tmp/meh");
if (Files.exists(path))
// see https://github.com/fge/java7-fs-more
MoreFiles.deleteRecursive(path, RecursionMode.KEEP_GOING);
Files.createDirectories(path);
final Path dbpath = path.resolve("foo");
final String db = "jdbc:h2:" + dbpath.toAbsolutePath();
final Connection connection = DriverManager.getConnection(db, "sa", "");
final DSLContext jooq = DSL.using(connection, SQLDialect.H2);
//jooq.dropTableIfExists("bad").execute();
final CreateTableColumnStep step = jooq.createTable("bad")
.column("f1", SQLDataType.INTEGER.nullable(false))
.column("f2", SQLDataType.INTEGER.nullable(false))
.column("fuckedup", SQLDataType.VARCHAR.length(20).nullable(false));
step.execute();
final Table<Record> table = DSL.tableByName("bad");
final Field<Object> f1 = DSL.fieldByName("f1");
final Field<Object> f2 = DSL.fieldByName("f2");
final Field<Object> f3 = DSL.fieldByName("fuckedup");
jooq.insertInto(table, f1, f2, f3)
.values(1, 2, "hello,world")
.execute();
// prints "1 2" as expected
jooq.select(f1, f2)
.from(table)
.forEach(System.out::println);
}
}
I have been looking at many a page and failed to achieve what I want, that is "unfsuck up" the f*ed up column; that is, I'd like to obtain this:
1 2 hello
1 2 world
I am not an SQL expert and am only used to work with properly normalized tables; this isn't one. How do I obtain the result above in SQL, knowing that I use:
jooq 3.5.1,
h2 as an SQL engine?
It looks to me like what is needed is a pivot of some sort, but I am unable to even split the string at the moment :/
Related
I am working on a complex query that contains a common table expression and multiple subqueries. I am trying to keep the code readable by splitting it into methods but I am struggling a bit.
Is there a way to return a custom JOOQ record when building a common table expression or subquery instead of the standard JOOQ Records?
An example of a common table expression:
public static final String COLUMN1 = "column_1";
public static final String COLUMN2 = "column_2";
public static final String COLUMN3 = "column_3";
public static final String COLUMN4 = "column_4";
public static final String COLUMN5 = "column_5";
public static final String COLUMN6 = "column_6";
public CommonTableExpression<Record6<Long, String, String, LocalDate, LocalDate, Boolean>> getMyFirstCTE() {
var t = MY_TABLE.as("t");
return name("t")
.fields(COLUMN1, COLUMN2, COLUMN3, COLUMN4, COLUMN5, COLUMN6)
.as(
select(
t.COLUMN1,
t.COLUMN2,
t.COLUMN3,
t.COLUMN4,
t.COLUMN5,
t.COLUMN6)
.from(t)
.where(t.COLUMN6.isFalse()));
}
and an example of a subquery:
public Table<Record6<Long, String, String, LocalDate, LocalDate, Boolean>> getMyFirstTable() {
var t = MY_TABLE.as("t");
return select(
t.COLUMN1,
t.COLUMN2,
t.COLUMN3,
t.COLUMN4,
t.COLUMN5,
t.COLUMN6)
.from(t)
.where(t.COLUMN6.isFalse()).asTable("t");
}
If the caller wants to use the fields from the common table expression it's expressed as shown below (same holds for the subquery):
var cte = getMyFirstCTE();
var column1 = cte.field(COLUMN1, Long.class);
var column2 = cte.field(COLUMN2, String.class);
var column3 = cte.field(COLUMN3, String.class);
var column4 = cte.field(COLUMN4, LocalDate.class);
var column5 = cte.field(COLUMN5, LocalDate.class);
var column6 = cte.field(COLUMN6, Boolean.class);
It would be nice to have these signatures instead:
public static CommonTableExpression<MyFirstRecord> getMyFirstCTE() {}
public static Table<MyFirstRecord> getMyFirstTable() {}
Not only for readability but also to (hopefully) not having to explicitly add the class type and be able to do something like MyFirstRecord.COLUMN1.
Is there a way to do this?
This is actually a re-do of an older question of mine that I have completely redone because my old question seemed to confuse people.
I have written a Java program that Queries a database and is intended to retrieve several rows of data. I have previously written the program in Informix-4GL and I am using a sql cursor to loop through the database and store each row into a "dynamic row of record". I understand there are no row of records in Java so I have ended up with the following code.
public class Main {
// DB CONNECT VARIABLE ===========================
static Connection gv_conn = null;
// PREPARED STATEMENT VARIABLES ==================
static PreparedStatement users_sel = null;
static ResultSet users_curs = null;
static PreparedStatement uinfo_sel = null;
static ResultSet uinfo_curs = null;
// MAIN PROGRAM START ============================
public static void main(String[] args) {
try {
// CONNECT TO DATABASE CODE
} catch(Exception log) {
// YOU FAILED CODE
}
f_prepare(); // PREPARE THE STATEMENTS
ArrayList<Integer> list_id = new ArrayList<Integer>();
ArrayList<String> list_name = new ArrayList<String>();
ArrayList<Integer> list_info = new ArrayList<String>();
ArrayList<String> list_extra = new ArrayList<String>();
try {
users_sel.setInt(1, 1);
users_curs = users_sel.executeQuery();
// RETRIEVE ROWS FROM USERS
while (users_curs.next()) {
int lv_u_id = users_curs.getInt("u_id");
String lv_u_name = users_curs.getString("u_name");
uinfo_sel.setInt(1, lv_u_id);
uinfo_curs = uinfo_sel.executeQuery();
// RETRIEVE DATA FROM UINFO RELATIVE TO USER
String lv_ui_info = uinfo_curs.getString("ui_info");
String lv_ui_extra = uinfo_curs.getString("ui_extra");
// STORE DATA I WANT IN THESE ARRAYS
list_id.add(lv_u_id);
list_name.add(lv_u_name);
list_info.add(lv_ui_info);
list_extra.add(lv_ui_extra);
}
} catch(SQLException log) {
// EVERYTHING BROKE
}
// MAKING SURE IT WORKED
System.out.println(
list_id.get(0) +
list_name.get(0) +
list_info.get(0) +
list_extra.get(0)
);
// TESTING WITH ARBITRARY ROWS
System.out.println(
list_id.get(2) +
list_name.get(5) +
list_info.get(9) +
list_extra.get(14)
);
}
// PREPARE STATEMENTS SEPARATELY =================
public static void f_prepare() {
String lv_sql = null;
try {
lv_sql = "select * from users where u_id >= ?"
users_sel = gv_conn.prepareStatement(lv_sql);
lv_sql = "select * from uinfo where ui_u_id = ?"
uinfo_sel = gv_conn.prepareStatement(lv_sql)
} catch(SQLException log) {
// IT WON'T FAIL COZ I BELIEEEVE
}
}
}
class DBConn {
// connect to SQLite3 code
}
All in all this code works, I can hit the database once, get all the data I need, store it in variables and work with them as I please however this does not feel right and I think it's far from the most suited way to do this in Java considering I can do it with only 15 lines of code in Informix-4GL.
Can anyone give me advice on a better way to achieve a similar result?
In order to use Java effectively you need to use custom objects. What you have here is a lot of static methods inside a class. It seems that you are coming from a procedural background and if you try to use Java as a procedural language, you will not much value from using it. So first off create a type, you can plop it right inside your class or create it as a separate file:
class User
{
final int id;
final String name;
final String info;
final String extra;
User(int id, String name, String info, String extra)
{
this.id = id;
this.name = name;
this.info = info;
this.name = name;
}
void print()
{
System.out.println(id + name + info + extra);
}
}
Then the loop becomes:
List<User> list = new ArrayList<User>();
try {
users_sel.setInt(1, 1);
users_curs = users_sel.executeQuery();
// RETRIEVE ROWS FROM USERS
while (users_curs.next()) {
int lv_u_id = users_curs.getInt("u_id");
String lv_u_name = users_curs.getString("u_name");
uinfo_sel.setInt(1, lv_u_id);
uinfo_curs = uinfo_sel.executeQuery();
// RETRIEVE DATA FROM UINFO RELATIVE TO USER
String lv_ui_info = uinfo_curs.getString("ui_info");
String lv_ui_extra = uinfo_curs.getString("ui_extra");
User user = new User(lv_u_id, lv_u_name, lv_ui_info, lv_ui_extra);
// STORE DATA
list.add(user);
}
} catch(SQLException log) {
// EVERYTHING BROKE
}
// MAKING SURE IT WORKED
list.get(0).print();
This doesn't necessarily address the number of lines. Most people who use Java don't interact with databases with this low-level API but in general, if you are looking to get down to the fewest number of lines (a questionable goal) Java isn't going to be your best choice.
Your code is actually quite close to box stock JDBC.
The distinction is that in Java, rather than having a discrete collection of arrays per field, we'd have a simple Java Bean, and a collection of that.
Some examples:
public class ListItem {
Integer id;
String name;
Integer info;
String extra;
… constructors and setters/getters ellided …
}
List<ListItems> items = new ArrayList<>();
…
while(curs.next()) {
ListItem item = new ListItem();
item.setId(curs.getInt(1));
item.setName(curs.getString(2));
item.setInfo(curs.getInfo(3));
item.setExtra(curs.getString(4));
items.add(item);
}
This is more idiomatic, and of course does not touch on the several frameworks and libraries available to make DB access a bit easier.
I improved the structure after a good observation from [http://stackoverflow.com/users/1690199/v-k] I am still getting a token error even though the syntax looks correct to me. More comments and critiques will be useful and acknowledged here.
import de.bezier.data.sql.*;
PostgreSQL pgsql;
Float val;
void setup()
{
size( 100, 100 );
println(val);
}
Token error identified in Processing 2 at Class Database.
Class Database
{
String user = "user";
String pass = "pass";
String database = "db";
Float val;
Database (Float col) {
val = col;
}
void database_connection( col )
{
//sets up database
pgsql = new PostgreSQL( this, "127.0.0.1", database, user, pass );
if ( pgsql.connect() )
{
pgsql.query( "SELECT col FROM table ORDER BY col DESC LIMIT 1; " );
return( pgsql.getFloat("col") );
}
else
{
println ("failed to connect to the database");
}
}
}
OLD ISSUE: Class structure addressed after a great observation from [http://stackoverflow.com/users/1690199/v-k]
import de.bezier.data.sql.*;
.....
.....
Old code removed for clarity of this issue.
Classes don't take arguments. Also it is class not Class... Am i missing something? Look, a general sample:
class Database {
String user = "user";
String pass = "pass";
String database = "db";
float val; //by convention no Caps for vars...
// a constructor, which get partameters
Database (float v) {
val = v;
}
// a method
void database_setup() {
//whateverq
}
}//end of Database class
First, you should create a new question if you have a second question. Second, you never create the variable pgsql, you just start using it immediately. Move this line:
PostgreSQL pgsql;
to this group of lines:
String user = "user";
String pass = "pass";
String database = "db";
float val;
It's a variable that gets used in that class, so put it in that class. Also, use "float" with a lowercase "f" :)
There are lots of questions on how to format the results of an SQL query to an HTML table, but I'd like to go the other way - given an arbitrary HTML table with a header row, I'd like to be able to extract information form one or more rows using SQL (or an SQL-like language). Simple to state, but apparently not so simple to accomplish.
Ultimately, I'd prefer to parse the HTML properly with something like libtidy or JSoup, but while the API documentation is usually reasonable, when it comes to examples or tutorials on actually using them, you usually find an example of extracting the <title> tag (which could be accomplished with regexes) with no real-world examples of how to use the library. So, a good resource or example code for one of the existing, established libraries would also be good.
A simple code for transforming a table into a list of tuples using JSoup looks like this:
public class Main {
public static void main(String[] args) throws Exception {
final String html =
"<html><head/><body>" +
"<table id=\"example\">" +
"<tr><td>John</td><td>Doe</td></tr>" +
"<tr><td>Michael</td><td>Smith</td>" +
"</table>" +
"</body></html>";
final List<Tuple> tuples = parse (html, "example");
//... Here the table is parsed
}
private static final List<Tuple> parse(final String html, final String tableId) {
final List<Tuple> tuples = new LinkedList<Tuple> ();
final Element table = Jsoup.parse (html).getElementById(tableId);
final Elements rows = table.getElementsByTag("tr");
for (final Element row : rows) {
final Elements children = row.children();
final int childCount = children.size();
final Tuple tuple = new Tuple (childCount);
for (final Element child : children) {
tuple.addColumn (child.text ());
}
}
return tuples;
}
}
public final class Tuple {
private final String[] columns;
private int cursor;
public Tuple (final int size) {
columns = new String[size];
cursor = 0;
}
public String getColumn (final int no) {
return columns[no];
}
public void addColumn(final String value) {
columns[cursor++] = value;
}
}
From this on you can e.g. create an in-memory table with H2 and use a regular SQL.
This question is related to my original issue How to return an array from Java to PL/SQL ?, but is a more specific.
I have been reading Oracle Database JDBC Developer's Guide and
Creating ARRAY objects
Server-Side Internal Driver
oracle.jdbc.OracleConnection
oracle.jdbc.OracleDriver
but I still fail to write a minimum code where I can create ARRAY using
ARRAY array = oracle.jdbc.OracleConnection.createARRAY(sql_type_name, elements);
as instructed in Creating ARRAY objects.
I'm using Oracle Database JVM.
I have tried following:
Example 1
create or replace type widgets_t is table of varchar2(32767);
/
create or replace and compile java source named "so20j1" as
public class so20j1 {
public void f1() {
String[] elements = new String[]{"foo", "bar", "zoo"};
oracle.sql.ARRAY widgets =
oracle.jdbc.OracleConnection.createARRAY("widgets_t", elements);
}
};
/
show errors java source "so20j1"
Fails with:
Errors for JAVA SOURCE "so20j1":
LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0 so20j1:4: non-static method
createARRAY(java.lang.String,java.lang.Object) cannot be
referenced from a static context
0/0 1 error
0/0 ^
0/0 oracle.sql.ARRAY widgets =
oracle.jdbc.OracleConnection.createARRAY("widgets_t", elements);
Example 2
create or replace type widgets_t is table of varchar2(32767);
/
create or replace and compile java source named "so20j2" as
public class so20j2 {
public void f1() {
String[] elements = new String[]{"foo", "bar", "zoo"};
oracle.jdbc.OracleDriver ora = new oracle.jdbc.OracleDriver();
java.sql.Connection conn = ora.defaultConnection();
oracle.sql.ARRAY widgets = conn.createARRAY("widgets_t", elements);
}
};
/
show errors java source "so20j2"
Fails with:
Errors for JAVA SOURCE "so20j2":
LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0 so20j2:6: cannot find symbol
0/0 symbol : method createARRAY(java.lang.String,java.lang.String[])
0/0 1 error
0/0 oracle.sql.ARRAY widgets = conn.createARRAY("widgets_t",
elements);
0/0 ^
0/0 location: interface java.sql.Connection
Disclaimer: I'm not a Java programmer (yet).
You're on the right track with #2, but you can't create an oracle Array from a connection of type java.sql.Connection. It has to be an OracleConnection to be able to use those methods.
oracle.jdbc.OracleDriver ora = new oracle.jdbc.OracleDriver();
java.sql.Connection conn = ora.defaultConnection();
OracleConnection oraConn = conn.unwrap(OracleConnection.class);
oracle.sql.ARRAY widgets = oraConn.createARRAY("widgets_t", elements);
Based on answers of Affe and Chris Mazzola I have succeeded to build two examples that compile in Oracle 11g R2 database.
Example based on Affe's answer
create or replace type widgets_t is table of varchar2(32767);
/
create or replace and compile java source named "so20ja1" as
public class so20ja1 {
public void f1() throws java.sql.SQLException {
String[] elements = new String[]{"foo", "bar", "zoo"};
oracle.jdbc.OracleDriver ora = new oracle.jdbc.OracleDriver();
java.sql.Connection conn = ora.defaultConnection();
oracle.jdbc.OracleConnection oraConn = (oracle.jdbc.OracleConnection)conn;
java.sql.Array widgets = oraConn.createARRAY("widgets_t", elements);
}
};
/
show errors java source "so20ja1"
Example based on Chris Mazzola's answer
create or replace type widgets_t is table of varchar2(32767);
/
create or replace and compile java source named "so20ja2" as
public class so20ja2 {
public void f1() throws java.sql.SQLException {
String[] elements = new String[]{"foo", "bar", "zoo"};
oracle.jdbc.OracleDriver ora = new oracle.jdbc.OracleDriver();
java.sql.Connection conn = ora.defaultConnection();
oracle.sql.ArrayDescriptor desc =
oracle.sql.ArrayDescriptor.createDescriptor("widgets_t", conn);
java.sql.Array widgets = new oracle.sql.ARRAY(desc, conn, elements);
}
};
/
show errors java source "so20ja2"
// array sample (using a stored procedure to sum two or more numbers)
Connection connection = dataSource.getConnection(username,password);
ArrayDescriptor desc =
ArrayDescriptor.createDescriptor(schemaName + "." + arrayType, connection);
// first ? is the array, second ? is the result via out parameter
String sql = "call sum_numbers(?,?)";
CallableStatement cs = connection.prepareCall(sql);
String[] args = {"5","15","25","35"}; // what to sum
Array array = new oracle.sql.ARRAY(desc, connection, args);
cs.setArray(1, array);
cs.registerOutParameter(2, Types.INTEGER);
cs.execute();
int result = cs.getInt(2);
cs.close();
Just to mention that in Java 1.6 you have connection.createArrayOf(..), which is standard.
Map<String, Object> params = new HashMap<>();
params.put("input_to_sp", new ExampleArrayMapper (new String[] { "ABC" }));
Map<String, Object> results = spObject.execute(params);
final List<Object> output = (List<Object>) results.get("po_out_cur");
public class ExampleArrayMapper extends AbstractSqlTypeValue {
private String[] customObject;
public ExampleArrayMapper (String[] customObject) {
this.customObject= customObject;
}
public String getSQlTypeName() throws SQLException {
return "NAME_OF_TYPE_IN_SQL";
}
#Override
protected Object createTypeValue(Connection con, int sqlType, String typeName) throws SQLException {
try {
con = dataSource.getConnection().unwrap(OracleConnection.class);
Array reportsArray = ((OracleConnection) con).createOracleArray(SQL_TYPE_NAME, customObject);
return reportsArray;
} finally {
con.close();
}
}
}
Oracle 12.2 version:
oracle.jdbc.OracleArray v_arr ;
String[] v_list ;
v_arr = ((oracle.jdbc.OracleConnection)DriverManager.getConnection("jdbc:default:connection:")).createARRAY ( "T_STRING_ARRAY" , v_list ) ;