How to make jOOQ quote table names that are reserved keywords? - java

On selecting from a table called user with jOOQ I get the following exception:
jOOQ; bad SQL grammar [insert into user (user_id, account_type) values (?, ?)]; nested exception is org.postgresql.util.PSQLException: ERROR: syntax error at or near "user"
My jOOQ settings are:
private static final Settings jooqSettings = new Settings()
.withRenderSchema(false)
.withRenderNameStyle(RenderNameStyle.LOWER);
I create a DSLContext from that and construct a query in a transaction as follows:
ctx.insertInto(USER)
.set(USER.USER_ID, userId)
.set(USER.ACCOUNT_TYPE, "U")
.execute()
USER is imported as <jooq-generated-package>.tables.USER.
Does jOOQ have a config property to escape table names (all or just reserved keywords)? I couldn't find anything in the docs or source.

Well, you turned that quoting off by setting RenderNameStyle.LOWER... That's how it works :)
By removing that setting or by setting it to RenderNameStyle.QUOTED, jOOQ will generate those double quotes around all identifiers.
From the specification:
<simpleType name="RenderNameStyle">
<restriction base="string">
<!--
Render object names quoted, as defined in the database. Use this
to stay on the safe side with case-sensitivity and special
characters. For instance:
Oracle : "SYS"."ALL_TAB_COLS"
MySQL : `information_schema`.`TABLES`
SQL Server: [INFORMATION_SCHEMA].[TABLES]
-->
<enumeration value="QUOTED"/>
<!--
Render object names, as defined in the database. For instance:
Oracle : SYS.ALL_TAB_COLS
MySQL : information_schema.TABLES
SQL Server: INFORMATION_SCHEMA.TABLES
-->
<enumeration value="AS_IS"/>
<!--
Force rendering object names in lower case. For instance:
Oracle : sys.all_tab_cols
MySQL : information_schema.tables
SQL Server: information_schema.tables
-->
<enumeration value="LOWER"/>
<!--
Force rendering object names in upper case. For instance:
Oracle : SYS.ALL_TAB_COLS
MySQL : INFORMATION_SCHEMA.TABLES
SQL Server: INFORMATION_SCHEMA.TABLES
-->
<enumeration value="UPPER"/>
</restriction>
</simpleType>
Note, there are feature requests to add more documentation to the Javadoc (#2830) and the manual (#5231)

Related

Column not found at liquibase migration for H2 in-memory database

I had to replace my PostgreSQL database with an H2 in-memory database and my migration started to fail. The create table script runs but the insert fails.
ChangeSet classpath:database/changelog/create-default-user.xml::createUserTable::ethero ran successfully in 7ms
Change Set classpath:database/changelog/create-default-user.xml::insertDefaultUser::ethero failed. Error: Column "username" not found; SQL statement:
Error Stacktrace
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.MigrationFailedException: Migration failed for change set classpath:database/changelog/create-default-user.xml::insertDefaultUser::ethero:
Reason: liquibase.exception.DatabaseException: Column "username" not found; SQL statement:
INSERT INTO usr ("username", "password") VALUES ('admin', 'password') [42122-200] [Failed SQL: (42122) INSERT INTO usr ("username", "password") VALUES ('admin', 'password')]
Create table script
create table user_authorities (username varchar(255) not null , authorities varchar(255));
create table usr (username varchar(255) not null , password varchar(255), image_url varchar(255))
Insert user script
INSERT INTO usr ("username", "password") VALUES ('admin', 'password');
INSERT INTO user_authorities ("username", "authorities") VALUES ('admin', 'ADMIN');
INSERT INTO user_authorities ("username", "authorities") VALUES ('admin', 'USER');
Spring boot configuration
spring:
profiles:
active: dev
datasource:
url: jdbc:h2:mem:pc;DB_CLOSE_DELAY=-1;
username: sa
password: sa
driver-class-name: org.h2.Driver
jpa:
generate-ddl: true
hibernate:
ddl-auto: update
show-sql: true
liquibase:
change-log: classpath:database/changelog-master.xml
url: jdbc:h2:mem:pc;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
Spring boot version: 2.4.3
PostgreSQL and few other DBMS are different from others: they convert unquoted identifiers to lower case. H2 and many others convert unquoted identifiers to upper case and such behavior is actually a standard one.
If you want to use both DBMS for a some reason, you can add ;DATABASE_TO_LOWER=TRUE to JDBC url of H2, with this parameter H2 will treat case of identifiers in the same way as PostgreSQL does.
But if you want to use only the H2, it would be better not to use such settings and adjust your SQL to use upper case letters in quoted identifiers.
Anyway, mixing quoted and unquoted identifiers is a bad practice. Usually it's better to always quote or never quote them. Note that there are various reserved words and these words cannot be used as unquoted identifiers.

How can I specify the current schema for sql server in a jboss data source url?

I've got some SQL queries like this
select user_id from
table_user where lower(email_address)=? and password=?
The schema for the application was recently updated but I don't really want to update every SQL query in the application. Is there a way to specify the current Schema from the JBOSS connection end?
Old connection: jdbc:sqlserver://myserver:1433;DatabaseName=db
Tried: jdbc:sqlserver://myserver:1433;DatabaseName=db;currentSchema=abc
I tried using currentSchema but that didn't help, I get a missing object exception when I run the queries (since I assume these are looking under dbo). Is there any way around updating the queries since I know that all the queries will run on schema abc?
These are the available connection properties for Microsoft JDBC 4.0 driver. I don't see currentSchema in this list, and haven't seen any driver that allows you to specify a particular schema in the connection string.
Since you don't want to update SQL with the schema, you could create synonyms in default (dbo) schema for each object. For example:
USE tempdb;
GO
-- create test schema
CREATE SCHEMA test AUTHORIZATION dbo;
GO
-- create table in test schema
CREATE TABLE test.tablename (columnname int null);
-- select from tablename in default schema will fail
SELECT * FROM tablename;
GO
-- create synonym mapping test.tablename to dbo.tablename
CREATE SYNONYM [dbo].[tablename] FOR [server].[tempdb].[test].[tablename]
-- -- select from tablename synonym will succeed
SELECT * FROM tablename;
-- cleanup
DROP SYNONYM [dbo].[tablename];
DROP TABLE [test].[tablename];
DROP SCHEMA [test];
You can use the below code to generate CREATE SYNONYM statements for user objects. If you use it, you'll need to update variable values and review statements before executing. No warranty express or implied :)
-- generate create synonym statements for user objects
DECLARE #FromSchema SYSNAME = 'abc',
#ToSchema SYSNAME = 'dbo',
#ServerName SYSNAME = 'server',
#DatabaseName SYSNAME = 'database';
SELECT 'CREATE SYNONYM ' + QUOTENAME(#ToSchema) + '.' + QUOTENAME(name) +
' FOR ' + QUOTENAME(#ServerName) + '.' + QUOTENAME(#DatabaseName) +
'.' + QUOTENAME(#FromSchema) + '.' + QUOTENAME(name) + ';'
FROM sys.objects
WHERE is_ms_shipped = 0;

Why is Hibernate generating this SQL query?

I'm using Hibernate and a MySql server. I use multiple databases as "namespaces" (e.g. users, transactions, logging etc.).
So, I configued Hibernate to NOT connect to a particular database :
url = jdbc:mysql://127.0.0.1/
The databases where tables are located are defined in the hbm files through the catalog attribute :
<class name="com.myApp.entities.User" table="user" schema="" catalog="users"> ...
When I want to load some data, everything works fine and Hibernate seems to generate the expected SQL queries (by using the catalog prefix in the table names) e.g. :
select id from users.user
However, when I try to add a new record, Hibernate don't use the from [catalog].[table_name] syntax anymore. So I get a MySQL error 'No database selected'.
select max(id) from user
Hibernate is trying the get the future id to create a new record, but it doesn't specify in which database is located the table, it should be :
select max(id) from users.user
Why is Hibernate generating this invalid query ? Have someone ever experienced this problem ?
You need to specify the schema for the generator. See this question on SO for a more detailed answer.

MyBatis executing multiple sql statements in one go, is that possible?

i was wondering if it is possible to execute multiple sql statements in 1 go.
For example the scenario that i want to delete rows from multiple tables, is there a way i can do things like..
<delete id="delete" parameterType="String">
DELETE FROM DUMMYTABLE_A where X=${value}
DELETE FROM DUMMYTABLE_B where X=${value}
</delete>
I'm using myBatis with Oracle. I guess there is something similar in other DB. Actually you always can create procedures in DB, which usually is better for the future, when you have to support the project.
<delete id="deleteUnfinishedData" parameterType="map">
{call
declare
begin
delete from TABLE1 where id = #{valueFromMap1};
delete from TABLE2 where id = #{valueFromMap2};
end
}
</delete>
Yes, most databases allow this. Usually you have to delimit your SQL statements with something. In PostGRES and MySQL it's a semicolon (;). In Microsoft SQL server you should use the keyword GO. [ May 2013 Update: As of SQL Server 2012, you can and should use semicolons to delimit your statements. After SQL Server 2012 (ie. the next version and beyond) these will be mandatory. Using GO is now the deprecated way to do things in SQL2012 and beyond). ]
MySQL / PostGRES example:
DELETE FROM DUMMYTABLE_A where X=${value};
DELETE FROM DUMMYTABLE_B where X=${value};
DELETE FROM DUMMYTABLE_C where X=${value};
MS-SQL example:
DELETE FROM DUMMYTABLE_A where X=${value}
GO
DELETE FROM DUMMYTABLE_B where X=${value}
GO
DELETE FROM DUMMYTABLE_C where X=${value}
Better databases (ie. not MySQL) will also support transactions with BEGIN TRAN / COMMIT TRAN / ROLLBACK TRAN. Using transactions you can actually batch all the statements into one atomic operation, where if part of it failed, all three would be rolled back. See http://www.sqlteam.com/article/introduction-to-transactions for some more information about those.
Most likely all you need is the semicolons between your SQL statements though!
if anyone got an error like
Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL
syntax; check the manual that corresponds to your MariaDB server
version for the right syntax to use near 'UPDATE mytable
You can fix this by allowing multi queries in your driver.
For mariadb it would be the same as MySQL
allowMultiQuery=true
described in following mybatis issue
https://github.com/mybatis/mybatis-3/issues/1497
This code works for multiple Select in one go in MSSQL:
<select id="selectMultipleQueries" resultSets="movies,genres" resultMap="multipleQueriesResult">
BEGIN
select M.ID_ as mId,
M.NAME_ as mName,
from TestMyBatis.dbo.Movie as M
where M.ID_ = #{id,jdbcType=INTEGER,mode=IN};
select G.ID_ as gId,
G.NAME_ as gName
from TestMyBatis.dbo.Genre as G
where G.ID_ = #{id,jdbcType=INTEGER,mode=IN};
END
</select>

JDBC connect string and Oracle synonyms

we have a Java program connecting via JDBC thin client to an Oracle 10g database.
Everything was working fine, but now the DBA wants us to connect with a different username/password, which is supposed to have access to the same tables using public synonyms.
Unfortunately the Java program no longer sees the tables (see error below when I try to do "select * from tablename").
I have tried to connect using the same username/password with Oracle SQL Developer and in this case I can run "select * from tablename" without problems.
Is there a specific Parameter I need to put in the connect string?
Many thanks!
Exception in thread "main" java.sql.SQLException: ORA-00942: table or view does not exist
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:743)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:207)
at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:790)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1037)
at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:830)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1132)
at oracle.jdbc.driver.OracleStatement.executeInternal(OracleStatement.java:1687)
at oracle.jdbc.driver.OracleStatement.execute(OracleStatement.java:1653)
Edited by: user555817 on 08-Oct-2010 04:55
You have to append Schema Name along with the table name and make it in capital letters (I dont remember if that is case-sensitive or just caps).
Example:
If there is an Employee Table in SCH1 and the synonym is created in SCH2 as Emp for SCH2.Employee, then the below statement is valid,
SELECT * FROM SCH2.emp
Where,
emp: Synonym Name
SCH2: Schema Name where this synonym is created, not the Schema Name of the actual table.

Categories