PostgreSQL JDBC Driver rounds crops double value - java

I'm using PostgreSql 9.3 together with the postgresql-9.3-1101 JDBC Driver.
And there is a really strange behavior with the PreparedStatements.
Here is the example:
First I create a test table:
CREATE TABLE double_test
(
id numeric(18) NOT NULL
);
After that that i run following code:
DecimalFormat df = new DecimalFormat("0");
try {
conn = BeanUtils.getBean("dataSource", DataSource.class).getConnection();
conn.setAutoCommit(false);
conn.createStatement().execute("DELETE from double_test ");
conn.commit();
PreparedStatement stmt = conn.prepareStatement("INSERT INTO double_test (id )VALUES (?)");
double input = 1234567890123456d;
System.out.println("Input:" + df.format(input));
stmt.setDouble(1, input);
stmt.execute();
conn.commit();
ResultSet rs = conn.createStatement().executeQuery("SELECT id FROM double_test ");
rs.next();
System.out.println("Output:" + df.format(rs.getDouble(1)));
rs.close();
} catch (SQLException e1) {
throw new RuntimeException(e1);
} finally {
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
The output of the code will be:
Input: 1234567890123456
Output:1234567890123460
Which gets me very confused, after the 15. digit the Postgres JDBC Driver starts to round. The inserted data gets corrupted, without any further notice!
Did I miss anything here? Has my code any errors?
Or is this a bug in the Driver? If so, I really wonder that no one noticed this yet.
Maybe working with BigDecimal will prevent the data from corrupting, but this is just a workaround.

There is a two problem.
First from Double class:
System.out.println(0.1 + 0.2); //output 0.30000000000000004
So usually don't use Double (also Float).
Second from jdbc driver , it is triying to save database with toString method which class related with org.postgresql.jdbc.PgPreparedStatement.
Check this:
System.out.println(new BigDecimal("0.00000072")); // print 7.2E-7
It is equal but when Jdbc driver save with this, some precisions corrupt, so I replace
public void setBigDecimal(#Positive int parameterIndex, #Nullable BigDecimal x)
throws SQLException {
setNumber(parameterIndex, x); // It is using toString method
}
with this:
public void setBigDecimal(#Positive int parameterIndex, #Nullable BigDecimal x)
throws SQLException {
checkClosed();
if (x == null) {
setNull(parameterIndex, Types.DECIMAL);
} else {
bindLiteral(parameterIndex, x.toPlainString(), Oid.NUMERIC); //now using toPlainString
}
}
If you really want to use Double or Float you can fix like this changes.

Related

Null Pointer Exception in Prepared Statement [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I ran in to a problem while trying to get data from my sql server where they give a null point exception at prepared statement. I'm sure this must be a noob question but please do help :)
this is the method i'm calling
public void notification(){
int MachCode = 1721;
try{
String sql ="Select TimeOn from PRODUCTIONS_MONITOR where MachCode='"+MachCode+"'";
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
while(rs.next()){
arrCount.add(rs.getInt(1));
}
for(int i=0;i<arrCount.size();i++){
Count = Count + arrCount.get(i);
}
if(Count % 10 == 0){
System.out.println("Time = " + Count);
}
}catch(SQLException e){
e.printStackTrace();
}
}
and here is my db connection
public static Connection ConnecrDb() {
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://10.228.59.2:1433;databaseName=dbNautilus;user=SA;password=KreedaIntimo#2017;");
System.out.println("Connected to database !");
} catch (SQLException sqle) {
System.out.println("Sql Exception :" + sqle.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("Class Not Found Exception :" + e.getMessage());
}
return null;
}
Your ConnecrDb() method assigns the new Connection object to a local variable named con, and then never use it.
The method then returns null.
You didn't show how to method was called, but it really doesn't matter, because the con field used by notification() method is either unassigned (and hence null), or assigned the null return value from ConnecrDb(), so it is null either way.
Given that, why are you confused the value is null and causes a NullPointerException?
Other general comment about your code:
If MachCode is an integer, then why are you quoting it in SQL, i.e. why where MachCode='"+MachCode+"'"; and not where MachCode="+MachCode;?
If you're using PreparedStatement, why not use ? parameter markers, as they are intended to be used?
You should use try-with-resources when using JDBC.
Java naming convention is for variable names to start with lowercase letter.
Your code should be:
int machCode = 1721;
String sql = "select TimeOn from PRODUCTIONS_MONITOR where MachCode = ?";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setInt(1, machCode);
try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) {
arrCount.add(rs.getInt(1));
}
}
}
// rest of code here

Error in JDBC code calling a stored function which returns a value

I'm trying to print the returned value of a MySQL stored function from the JDBC code which is as follows (I am using MySQL Server 5.7.13):
package jdbc;
import java.sql.DriverManager;
import java.sql.*;
import java.util.Scanner;
public class CallableStatementsCallingFunctions {
public static void main(String... syrt)
{
try
{
try
{
Class.forName("com.mysql.jdbc.Driver");
}
catch(ClassNotFoundException e)
{
System.out.println("Error(class): "+ e);
}
try
{
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/collablestatement","root","mysql") ;
CallableStatement cs = conn.prepareCall("{call ?:=getBalance1(?)}");
String s = new Scanner(System.in).nextLine();
cs.registerOutParameter(1,Types.INTEGER);
cs.setInt(2,Integer.parseInt(s));
cs.execute();
System.out.println("Account number :" + cs.getInt(1));
conn.close();
}
catch(SQLException e)
{
System.out.println("Error(SQL) : "+e);
}
}
catch(Exception e)
{
System.out.println("Error(Fro outer try) : "+ e);
}
}
}
the stored function getBalance1(acno) is shown here
my code output is shown here
I am getting the output from the SQL command but in JDBC I am getting and SQLException saying that
parameter 1 is not an output parameter
I know that parameter 1 has been used as the placeholder of the returned value from the function in jdbc code. In prepareCall I also tried the syntax - {?:= call getBalance1(?)} , but even then getting the same Exception.
Why am I getting the exception?
I think I was getting the SQLException because I am using jdk1.8.xx in which the syntax of calling the stored function is different. The problem was solved by replacing statement
CallableStatement cs = conn.prepareCall("{call ?:=getBalance1(?)}");
in the code with
CallableStatement cs = conn.prepareCall("{? = call getBalance1(?)}");
The syntax of calling the function in the prepareCall() method as parameter is here.
getBalance1() is a MySQL FUNCTION, not a PROCEDURE, so I wouldn't expect using a JDBC CallableStatement to be applicable.
Even in your MySQL console test you are using
select getBalance1(103)
so you simply need to do the same thing in your Java code using a PreparedStatement:
PreparedStatement ps = conn.prepareStatement("select getBalance1(?)");
ps.setInt(1) = 103;
ResultSet rs = ps.executeQuery();
rs.next();
Double bal = rs.getDouble(1);
(It should be noted that since "balance" apparently refers to "money", REAL is not a good choice for the column type; details here.)

Java, double returning function, what to return on failure?

I am moving from PHP to Java and I'm a little struggled.
I have this method like this that I use to get some data from MySQL database and I would like to treat the failure if no data got from database.
public double getRate() {
double ret;
try {
// do a select query
PreparedStatement stmt = conn.prepareStatement("SELECT `rate` FROM `rates` LIMIT 1");
ResultSet rs = stmt.executeQuery();
// get result
rs.absolute(1);
ret = rs.getDouble(1);
// close the resources
rs.close();
stmt.close();
}
// this catches only the SQL errors (if I am right)
catch (SQLException ex) {
ex.printStackTrace();
}
// THIS IS WRONG BECAUSE "variable ret might not have been initialized"
return ret;
}
In PHP we can return whatever in case of failure like this:
<?php
public function getRate() {
$ret = db::getOne("SELECT `rate` FROM `rates` LIMIT 1");
if ($ret) {
return $ret; // row has been found, so we return it
} else {
return false; // row hasn't been found, so we return false --> THIS is what I need to do in Java
}
}
?>
So how to treat a failure in Java methods/functions where I have nothing to return?
You have several options:
throw an exception, and catch this by the code which calls the method. This works well, is a nice way to handle it. But requires a lot of additional try-catch statements
Return -1 on error. This is also a very common way to do if you work with natural numbers only
always return a result object, which contains a the output and a success/error status
Use the Double Class instead, and return null on fail
In Java, you can't return double from one place and boolean from another. What you could do is, initialize your Double (wrapper of double primitive) value like:
Double ret = null;
And if there are no rows or any SQLException, you would return this value back to caller. In called method you could do something like:
Double rate = getRate();
if (rate == null) {
//no row found
} else {
//i have the row. continue with business logic
}
You could make your method return an object of the double wrapper class Double. Then you could return a null pointer in case of some failure.
public Double getRate() {
...
if(ok)
return new Double(ret);
else
return null;
}
Initialize your double variable with a control value. If it is not changed when exiting the method, then something went wrong.
The control value can be something you do not expect to get from the query, so for rates it could be a negative number, say -1 since rates can't be negative.
double ret=-1.00d;
I am adding a sample code so you can understand how to handle such scenarios.
If your default value does not changes,it means there was nothing that matched your query.
public double methodName(int arg)
{
double risk=0.0;
String query = null;
PreparedStatement stm = null;
ResultSet r = null;
Connection con=null;
try{
con=ConnectionDB.getConnection();
if(con!=null)
{
query="select risk from table where year="+arg;
stm = con.prepareStatement(query);
r = stm.executeQuery();
if(r.next())
{
risk=r.getDouble(1);
}
}
}catch(Exception e)
{
e.printStackTrace();
}
finally{
try {
if (r != null) {
r.close();
}
if (stm != null) {
stm.close();
}
if(con!=null)
{
con.close();
}
} catch (Exception e) {
System.out.println("" + e);
}
}
return risk;
}
You could return an OptionalDouble, which makes it obvious to the caller that they need to handle the case of the result not being found:
try {
// get ret
return OptionalDouble.of(ret);
} catch (SQLException ex) {
return OptionalDouble.empty();
}
In your Java example, when you talk about a "failure", you are talking about an unexpected error (e.g. a SQL Exception, a non-expected error in the DB access).
Nevertheless, in your PHP example, when you talk about a "failure", you are talking about a normal scenario (No data in database).
So, both examples are quite different.
In my opinion, if I get an unexpected sitution, I wouldn't return any value, I'd throw an exception. I usually return null, -1 and this kind of values in normal and expected scenarios where there isn't data to return.

Java : SQL syntax error for entering values in Table

i am trying to add two methods to withdraw and deposit money in Bank Class . My Database name is javatest . table name is bank and following is the code . Problem is that when i run this code compiler says You have an error in your SQL syntax; i did check code 3-4 times but really unable to get it please help me with it .
public static void main(String[] args)
{
Connection connection= null ;
Statement stmt = null ;
try
{
Class.forName("com.mysql.jdbc.Driver");
connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest","root","");
stmt= connection.createStatement();
withdrawfromchecking(connection, stmt, new BigDecimal(100), 1);
Depositinsaving(connection, stmt, new BigDecimal(444), 1);
stmt.executeBatch();
System.out.println("Done");
}
catch (ClassNotFoundException e) {e.getMessage();}
catch (SQLException e) {e.printStackTrace();}
finally
{
if(connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}
if(stmt!=null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}
}
}
public static void withdrawfromchecking(Connection connection ,Statement stmt, BigDecimal amount , int id ) throws SQLException
{
stmt.addBatch("UPDATE bank SET checkingbalance = checkingbalance-"+amount+"WHERE id="+id);
}
public static void Depositinsaving(Connection connection ,Statement stmt, BigDecimal amount , int id ) throws SQLException
{
stmt.addBatch("UPDATE bank SET savingbalance = savingbalance+ "+amount+"WHERE id="+id);
}
}
Error comes for this line - stmt.executeBatch(); when i run program
EDIT : Exact error statement
java.sql.BatchUpdateException: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near 'id =1' at line 1 at
com.mysql.jdbc.StatementImpl.executeBatch(StatementImpl.java:1193) at
MyPackage.BankAccount.main(BankAccount.java:24)
in my code (line 24 is stmt.executeBatch();
In both of your SQLs, there is no space between the concatenation of the amount and the word WHERE -- it looks like this: checkingbalance-100WHERE id=.
Place a space before both WHERE words.
stmt.addBatch("UPDATE bank SET checkingbalance = checkingbalance-"
// +- Add space here
// v
+amount+" WHERE id="+id);
Change your withdrawfromchecking and Depositinsaving methods to this:
public static void withdrawfromchecking(Connection connection, Statement stmt, BigDecimal amount, long id) throws SQLException{
statement.addBatch("UPDATE bank SET checkingBalance = checkingBalance - " +amount+ " WHERE id =" + id);
}
public static void Depositinsaving(Connection connection, Statement stmt, BigDecimal amount, long id) throws SQLException{
statement.addBatch("UPDATE bank SET savingBalance = savingBalance + " +amount+ " WHERE id =" + id);
}
The first step would be to put the update statement in a string and examine the value after concatenation.
Ideally you should be using parameterized prepared statements instead of dynamically concatenating the sql.

java mysql count number of rows

I created this code to allow me calculate the number of rows in my table. However, I'm not able to return the counted number with an error saying "cannot return a value from method whose result type is void." Could someone show me where' my error? Thanks alot!
public void num() throws Exception {
try {
// This will load the MySQL driver, each DB has its own driver
Class.forName("com.mysql.jdbc.Driver");
// Setup the connection with the DB
connect = DriverManager.getConnection("jdbc:mysql://localhost/testdb?"
+ "user=root&password=");
// Statements allow to issue SQL queries to the database
statement = connect.createStatement();
resultSet = statement.executeQuery("select * from testdb.emg");
int count = 0;
while (resultSet.next()) {
count++;
}
return count;
} catch (Exception e) {
}
Try below code
public int num() throws Exception {
try {
// This will load the MySQL driver, each DB has its own driver
Class.forName("com.mysql.jdbc.Driver");
// Setup the connection with the DB
connect = DriverManager.getConnection("jdbc:mysql://localhost/testdb?"
+ "user=root&password=");
// Statements allow to issue SQL queries to the database
statement = connect.createStatement();
resultSet = statement.executeQuery("select count(*) from testdb.emg");
while (resultSet.next()) {
return resultSet.getInt(1);
}
} catch (Exception e) {
}
Below were error
public void num() throws Exception {
should be
public int num() throws Exception {
For counting total rows you should use query select count(*) from testdb.emg
Let me know incase of any problem.
Change
public void num() throws Exception {
to
public int num() throws Exception {
You are returning value from variable count which is of type int therefore the return type of the method should be int as well.
You should also make sure there is a return statement in every execution path through your code including the exception handler in the catch blocks (or you will get a "missing return statement" error message). However, it is best to avoid catch statements which catch all exceptions (like yours). Also, ignoring (i.e. not handling) exceptions in the catch block often leads to hard to diagnose problems and is a bad practice.
There are also other problems with the code: with the exception of count none of your variables have been declared.
Note that you may use the following SQL statement to obtain the number of rows directly:
select count(*) from testdb.emg
This avoids sending all of the data from table testdb.emg to your application and is much faster for big tables.
How to get count(*) mysql data table in java.
TRY IT:
public int getRowNumber(){
int numberRow = 0;
Connection mysqlConn = DriverManager.getConnection(HOST, USER_ID, PASSWORD);
try{
mysqlConn.getConnection();
String query = "select count(*) from dataTable";
PreparedStatement st = mysqlConn.preparedStatement(query);
ResultSet rs = st.executeQuery();
while(rs.next()){
numberRow = rs.getInt("count(*)");
}
}catch (Exception ex){
System.out.println(ex.getMessage());
}
return numberRow;
}
public void num() throws Exception {
should be
public int num() throws Exception {
I use Fahim Parker answer with a bit change
`
public int num() throws Exception {
try {
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jdbc:mysql://localhost/testdb?"
+ "user=root&password=");
statement = connect.createStatement();
resultSet = statement.executeQuery("<your query statement>");
resultSet.last(); //go to last row;
return resultSet.getRow(); //get row number which is equal to rows count
} catch (Exception e) {
}
`

Categories

Resources