JDBC, MySQL: getting bits into a BIT(M!=1) column - java

I'm new to using JDBC + MySQL.
I have several 1/0 values which I want to stick into a database with a PreparedStatement. The destination column is a BIT(M!=1). I'm unclear on which of the setXXX methods to use. I can find the references for what data comes out as easily enough, but how it goes in is eluding me.
The values effectively live as an ordered collection of booleans in the objects used by the application. Also, I'll occasionally be importing data from flat text files with 1/0 characters.

To set a BIT(M) column in MySQL
For M==1
setBoolean(int parameterIndex, boolean x)
From the javadoc
Sets the designated parameter to the
given Java boolean value. The driver
converts this to an SQL BIT value when
it sends it to the database.
For M>1
The support for BIT(M) where M!=1 is problematic with JDBC as BIT(M) is only required with "full" SQL-92 and only few DBs support that.
Check here Mapping SQL and Java Types: 8.3.3 BIT
The following works for me with MySQL (at least with MySQL 5.0.45, Java 1.6 and MySQL Connector/J 5.0.8)
...
PreparedStatement insert = con.prepareStatement(
"INSERT INTO bittable (bitcolumn) values (b?)"
);
insert.setString(1,"111000");
...
This uses the special b'110101010' syntax of MySQL to set the value for BIT columns.

You can use get/setObject with a byte array (byte[]). 8 bits are packed into each byte with the least significant bit being in the last array element.

Related

SQL syntax error in H2 Database when inserting array

I am actually trying to insert the data in the H2 database. While starting up the application server, was getting the SQL syntax error exception. I am really not sure if H2 database supports the insertion of array in the column? Is there any issue with the below sql statement? Does H2 database support any of the array datatypes float[], String[]...?
INSERT INTO weather (id,date,temperature) values ('1','2019-09-11','{"37.3","36.8","36.4"}');
CREATE TABLE WEATHER(
id INT AUTO_INCREMENT PRIMARY KEY,
date DATE,
temperature text[]
);
You can't use PostgreSQL-style text[] as a data type in H2 and in other databases. H2 has the ARRAY data type for arrays:
https://h2database.com/html/datatypes.html#array_type
H2 1.4.201 will also support standard-compliant array data type with a component type:
componentDataType ARRAY[maximumCardinality]
You can build H2 from its current sources if you really need that functionality right now, but I think you don't really need it, non-standard plain ARRAY will work too.
'{"37.3","36.8","36.4"}' is a character string literal. H2 uses the standard array literals:
ARRAY[element, …]
https://h2database.com/html/grammar.html#array
If you use some outdated version of H2 you need to use non-standard (element, …) literal instead (but don't use that variant in recent versions, it will be parsed by them as a row value as required by the Standard).
It's not related with your question, but you really should use 1 instead of '1' as integer literal and DATE '2019-09-11' instead of '2019-09-11' as a date literal to avoid conversions from character strings to other data types.

Java / Sql-server parameter binding does not work as expected

We notice a strange behaviour in our application concerning bind parameters. We use Java with JDBC to connect to a Sql Server database. In a table cell we have the value 'µ', and we compare it with a bind parameter, which is also set to the value 'µ'.
Now, in a sql statement like "... where value != ?", where 'value' is the value of 'µ' in the database and ? the bind variable, which is also set to 'µ', we notice that we get a record, though we would expect that 'µ' equals 'µ'.
The method that we use to fill the bind parameter is java.sql.PreparedStatement.setString(int, String).
Some facts:
The character value of µ in different encodings is:
ASCII (ISO-8859-1) : 0xB5
UTF-8 : 0xC2B5
UTF-16 (= Java) : 0x00B5
Now I did some investigations to see which bytes the database actually sees. Therefor I tried a sql-statement like this:
select convert(VARBINARY(MAX), value), -- selects µ from database table
convert(VARBINARY(MAX), N'µ'), -- selects µ from literal
convert(VARBINARY(MAX), ?) -- selects µ from bind parameter
from ...
The result for the three values is:
B500
B500
C200B500 <-- Here is the problem!
So, the internal representation of µ in the database and as NVARCHAR literal is B500.
Now we can't understand what is going on here. We have the value of 'µ' in a Java variable (which should internally be 0x00B5). When it is passed as bind variable, then is seems as if it is converted to UTF-8 (which makes byte sequence 0xC2B5), and then the database treats it as if it were two characters, making the sequence of characters C200B500 from it.
To make things even more confusing:
(1) On an other machine with a different database the same code works like expected. The result of the three lines is B500/B500/B500, so the bind variable is converted to be a proper B500.
(2) On the same machine, the same database but a different program (but using the same jdbc driver library and the same connect parameters) this also works as expected, giving the result of B500/B500/B500.
Some additional facts, maybe they are important:
The database is Sql Server 2014
Java is Java 7
The application in question is a webapp running in Tomcat 7.
Jdbc library is sqljdbc 4.2
Any help to sort this out is greatly appreciated!
I now found the solution. It did not at all have something to do with Sql Server or binding, but instead...
Tomcat 7 is not running in UTF-8 mode by default (I wasn't aware of that). The µ we are talking about comes from an other application that is providing this value via webservice calls. However, this application is using UTF-8 as default. So, it was sending an UTF-8 µ, but the webservice did not expect UTF-8 and thought that it would be two characters, and treated them like this, filling the internal String variable with the character for 0xC2 and 0xB5 (which is, for Sql Server, C200B500).

How to determine size of fixed length array database column using JOOQ?

Given this PostgreSQL table with a fixed length array column :
CREATE TABLE test (
id integer,
values integer[4],
);
Will JOOQ code generation create a java constant or method that provides the max number of elements that can be stored in the values column (i.e. 4)?
After reading through JOOQ documentation on code generation and support for SQL arrays, I couldn't find anything specific about fixed length arrays. Also, nothing jumps out at me in the generated code that provides this information.
No, version 3.9 of jOOQ doesn't know any fixed size or size limit of a database array (neither with PostgreSQL array types nor with Oracle VARRAY types).
I have registered feature request #5932 for this.
I asked this question in part because I was worried about array overruns in the PostgreSQL database. After researching a way to use straight SQL to determine the size constraint, I noticed the PostgreSQL ARRAY documentation makes the statement :
As before, however, PostgreSQL does not enforce the size restriction in any case.
Based on that statement, it would appear using the array constraint to enforce size is not necessary since all array columns appear to be treated as variable length. So even if one could retrieve the PostgreSQL array size constraint through JOOQ, Straight SQL, or any other means, why bother?

Fetching long value from Database

My database contains a field ARR_ID with data type as NUMBER(20),
the value is = 100013085001
Some java application is fetching this value and they are using Integer as the datatype to fetch the value.
In the output of their application the value is displayed as 1228837193.
How is this value getting converted in Java I do not know?
What happens when the data is too large to be contained in the datatype?
Shouldn't the application throw an error in that case?
It depends on the SQL driver you are using. I saw the circumstances you are describing once when I was using the SQLite driver. I was also a bit concerned and checked the sources. The datatype is determinded by the value in the column. If it doesn't fit in an Integer a Long is used.
I don't remember how this is working for a collection. You may want to check the sources of the driver which you are using (if it's also open source).
If you use JDBC then with a 20-digit integer (e.g. 12345678901234567890), the program throws com.mysql.jdbc.exceptions.jdbc4.MySQLDataException: '1.2345678901234567E19' in column '1' is outside valid range for the datatype INTEGER. It seems you use Oracle which I don't have, this is for mysql, you may try for yourself.

Truncating strings

I'm working with third party user data that may or may not fit into our database. The data needs to be truncated if it is too long.
We are using IBatis with Connector/J. If the data is too long a SQL exception is thrown. I have had two choices: either truncate the strings in Java or truncate the strings in sql using substring.
I don't like truncating the strings in sql, because I am writing table structure in our Ibatis XML, but SQL on the other hand knows about our database collation (which isn't consistent and would be expensive to make consistent) and can truncate string in a multibyte safe manner.
Is there a way to have the Connector/J just straight insert this SQL and if not which route would people recommend?
According to the MySQL documentation it's possible that inserting data that exceeds the length could be treated as a warning:
Inserting a string into a string
column (CHAR, VARCHAR, TEXT, or BLOB)
that exceeds the column's maximum
length. The value is truncated to the
column's maximum length.
One of the Connector/J properties is jdbcCompliantTruncation. This is its description:
This sets whether Connector/J should
throw java.sql.DataTruncation
exceptions when data is truncated.
This is required by the JDBC
specification when connected to a
server that supports warnings (MySQL
4.1.0 and newer). This property has no effect if the server sql-mode includes
STRICT_TRANS_TABLES. Note that if
STRICT_TRANS_TABLES is not set, it
will be set as a result of using this
connection string option.
If I understand correctly then setting this property to false doesn't throw the exception but inserts the truncated data. This solution doesn't require you to truncate the data in program code or SQL statements, but delegates it to the database.

Categories