Java ResultSet adding zeros - java

Weird behavior here.
On the SQL Server database the columns type are Numeric(20,6) but the resultset retrieves the values added by a bunch of zeros:
Table: 952.346,44 - 890.00
Resultset: 952346.440000 -> 890000.000000
The application is running on WAS 8.5, Java 7 with JDBC Driver sqljdbc4-4.1.jar

As you have mentioned Numeric(20,6) which means the scale of the number is 6
And thats the reason the same precision is considered in Java.
Now if you want to reduce it 2 decimal places, you can simply update column type or if it is already in production you can simply reduce the number in JAVA with various possible ways, on of which is as follows:
Double.parseDouble(String.format("%.2f", 952346.440000));

Your column is a NUMERIC(20,6), the 6 there is the scale, and that specifies the number of decimals. In other words, the values already were 952346.440000 and 890000.000000, and JDBC just returned them according to specification.
If you need only 2 decimals, then use NUMERIC(20,2). Or rescale the number in your code using one of the BigDecimal.setScale methods.

Related

Java JDBC type conversion (Firebird / Jaybird): check value/type compatibility before using getter or updater ResultSet method?

If I understand (and test a sample JDBC code; using Jaybird for Firebird) well, even using a proper (= respecting the type mapping) updater method (e.g. ResultSet.updateString), or maybe PreparedStatement parameter, can bring a "conversion exception".
Is it possible (and is it a good practice) to test before actually working with the result set (e.g. running an updater method) whether the actual Java Type/value can be safely converted to the target SQL data type?
Is the "problem" just one-way? I.e. when converting back from SQL to Java (using getter method), is it guaranteed that the correct getter method will never fail (due to conversion problems)?
My examples (Using Jaybird 3.0.2, JDK1.8):
I need to update field: NUMERIC(9,2). The corresponding updater is:
ResultSet.updateBigDecimal(int columnIndex, BigDecimal x). If I use x = new BigDecimal("123456789.1234") (bigger precision and scale), I (logically) get an exception:
Exception in thread "main" org.firebirdsql.jdbc.field.TypeConversionException: Error converting to big decimal.
I need to update field: VARCHAR(5). The corresponding updater is: ResultSet.updateString(int columnIndex, String x).
If I use x = "123456" (longer string 6 > 5), I (logically) get an exception: Exception in thread "main" java.sql.DataTruncation: Data truncation.
Is there some general elegant way (not depending on specific type) how to check, whether an actual Java value/object can be "saved" to certain SQL field, other than just trying to run the query and catching the exceptions?
I would like to check the values already in my data editing dialog (before actually running the update query). Simple test "VALUE OK / NOT OK" would be fine (knowing just the target SQL type).
It seems quite difficult for me to find all rules I would have to check "type by type" (i.e. for VARCHAR check string length, for NUMERIC check precision and scale etc. - but what else? or would that be sufficient? for integer and float types no need to check anything?).
I tried to go through the Jaybird source codes but the "conversion process" is very complicated (and type-specific), I could not find the answer myself.
JDBC does not provide anything to 'check' values before you actually set them, so Jaybird doesn't either: setting the value is the check. Exact behaviour is driver dependent, Jaybird attempts to validate on setting values, but other drivers might choose to defer this to the database itself (so the error would only occur on execute).
Normally, you would design your database and pick column types based on your business needs, which should naturally have lead to validation before you even try to put it in the database.
If you haven't done that until now, start adding validation to your input forms, by restricting lengths, using things like Hibernate Validator, or the validation of your UI framework.
If you are working with highly dynamic requirements (eg user provided queries, etc), then you should use the features that JDBC does provide to create your own validation: The ParameterMetaData of a prepared statement and the ResultSetMetaData of a result set (also accessible from a prepared statement), specifically the getPrecision (and getScale) of these objects, or maybe even things like DatabaseMetadata.getColumns.
For a character type, getPrecision will indicate the max number of characters, for a numeric or decimal type you can use the max numbers of digits before the decimal point as precision - scale.
However in Jaybird this is not a 100% exact, for example getPrecision may return 9 for a numeric(8,2) if Jaybird can't identify the underlying column, and Jaybird (and Firebird) will actually allow up to precision 10 with some limitations (that is, unscaled max value of Integer.MAX_VALUE, ie 21474836.47 for this type).
As to your second question if using getters could cause a conversion exception: normal cases will not, but for example calling getInt() on a BIGINT with a value larger than Integer.MAX_VALUE or Integer.MIN_VALUE will.

How to keep sequence of 1s and 0s in a column on MySQL?

I need to store a list of 1s and 0s in a single column on MySQL. For that column max length of the sequence for a cell will be 100.
On java side, after I fetch that data from MySQL, I need to convert it to something like EvictingQueue or any other appropriate data type. Because I need to add new values while the earliest entered one (1 or 0) will be popped from the list. Then I will update MySQL with new sequence.
What data type do you suggest me to use on MySQL for that column? Also do you have any other suggestion for Java side? So far, EvictingQueue looks logical to me.
Solution: I preferred using EvictingQueue on Java side and VARBINARY(100) on MySQL side. Works stable.
For MySQL, varchar type makes more sense as any numeric type may trim out leading zeros.
For java, I think we can have the value extracted as String and do bitwise shifting as explained here.
EvictingQueue is perfectly fine. However, I thought of providing another approach.

How to perform a Restrictions.like on an integer field

I have an integer field in the DB (Postgresql) and my hibernate mapping file that I want to use in a like operation (e.g. Restrictions.like(Bean.fieldname,'123')).
The database does not support like for integer without explicit type casting select * from table where text(myint) like '1%'. Ideally, I'd like to keep the DB field type and Hibernate property type as integers and not have to load all the fields from the DB to iterate through in the Java code.
cheers :)
If the value really is a number, I'd just restrict it to a range - e.g. greater than or equal to 100 and less than 200. I wouldn't have thought you'd really want "all numbers starting with 1" - that suggests that 1 and 10000 are similar, whereas 1 and 2 are totally different. The information in a number should almost always relate to its magnitude, not the digits from its decimal representation.
Why do you need a LIKE? It's a very strange comparison, that's also why it's not an integer operator.
You could cast the value in the database to text/varchar, but you will kill performance unless you create a special index as well.
Restrictions.sqlRestriction("CAST({alias}.myint AS CHAR) like ?", "%1%", Hibernate.STRING));

Delete a row where its primary key is closest to X

I am attempting to delete a row, whose primary key(column name=version) for example is 4.002000000000001. So I give say the statement:
DELETE FROM tbl WHERE version=4.002;
Now right now this wont work, because theres no row that has a version equal to 4.002, only a row with a version equal to 4.002000000000001. Please dont ask why I dont store the version as 4.002 in the DB because I am trying to do so by making the column type DECIMAL(5,3) but it still stores a double from Java as 4.002000000000001. And please dont ask why I dont say "...WHERE version=4.002000000000001;" because right now I have no control over how java formats it doubles(even using DecimalFormat class) when I create a statement using JDBC.
So is there a way to round down(floor function) in SQLite3? So then I can say "delete the row whose FLOOR(version) is equal to 4.002?
I am a C++ programmer (using Java to query an SQL database, please dont ask why :P) who is not proficient with complex SQL queries, so would this be correct for what I want to do:
DELETE FROM tbl WHERE ROUND(version, 0.5)=4.002;
And please dont ask why I dont say "...WHERE version=4.002000000000001;" because right now I have no control over how java formats it doubles(even using DecimalFormat class)
I won't.
But I will ask why you are using double to store a version number.
Machine floating point types (base 2) do not hold exact decimal numbers. But version number strings are exact. You would be better off treating version "numbers" as one of the following, depending on your version numbering scheme:
Scaled integers; e.g. 4.002 is 4002 divided by 1000. You probably don't need to store the scaling factor explicitly.
Tuples or arrays of integers; e.g. 4.002 is {4,2}. This allows version numbers like 4.003.005 to be represented as {4,3,5} etcetera.
Character strings constrained by a pattern; e.g. 4.002 is a string that matches the regex "\d+\.\d\d\d". This allows version strings with non-numeric parts.
Choose one of those and you won't have to worry about the fundamental inexact nature of floating point types, in memory or in the database.
Oh, and by the way the DecimalFormat class does let you control the number of digits after the decimal place when you format a number. You can specify this using a pattern or by calling setMaximumFractionDigits
Can you do?
DELETE FROM tbl WHERE version > 4.002 - 1e-4 AND version < 4.002 + 1e-4
or
DELETE FROM tbl WHERE version > 4.0015 AND version < 4.0025
DELETE FROM tbl WHERE abs(version-4.002)<.0001
if you want to remove all versions in an epsilon boundary of .0001

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

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.

Categories