JOOQ insert with type - java

I'm using jOOQ 3.8.4 and PostgreSQL 9.5 in a Spring 4 application. I have the following table and type definition
CREATE DOMAIN shop.money_amount AS numeric(6,2) DEFAULT 0 NOT NULL CHECK (value > 0::numeric);
CREATE TYPE shop.money AS (
m_amount shop.money_amount,
m_currency shop.currency,
m_country shop.site_country
);
CREATE TYPE shop.money_mapping AS (
mm_moneys shop.money []
);
CREATE TABLE shop.article
(
a_id bigserial NOT NULL,
a_price shop.money_mapping NOT NULL,
CONSTRAINT a_pk_id PRIMARY KEY (a_id)
);
Then I tried an insert using jOOQ, i.e.:
MoneyMappingRecord priceMoneyMapping = new MoneyMappingRecord();
priceMoneyMapping.setMoneys(new MoneyRecord[]{
new MoneyRecord().setAmount(new BigDecimal("11")).setCountry(SiteCountry.US).setCurrency(Currency.USD),
new MoneyRecord().setAmount(new BigDecimal("14")).setCountry(SiteCountry.DE).setCurrency(Currency.EUR)
});
dsl.insertInto(ARTICLE)
.set(ARTICLE.A_PRICE, priceMoneyMapping)
.returning(ARTICLE.A_ID).fetchOne().getId();
Then I get:
Caused by: org.springframework.jdbc.BadSqlGrammarException: jOOQ;
bad SQL grammar [insert into "shop"."article" ("a_price") values
(row(?::money[])) returning "shop"."article"."a_id"]; nested
exception is org.postgresql.util.PSQLException:
ERROR: cannot cast type record to shop.money_mapping
Detail: Cannot cast type money[] to shop.money[] in column 1.
What am I doing wrong?
UPDATE
I tried to rename the shop.money type as suggested by Lukas (from shop.money to shop.localized_money), but I believe that the problem is related to the schema. See the updated error.
Caused by: org.springframework.jdbc.BadSqlGrammarException:
jOOQ; bad SQL grammar [insert into "shop"."article" ("a_price")
values (row(?::localized_money[])) returning "shop"."article"."a_id"]; nested exception is
org.postgresql.util.PSQLException: ERROR: type "localized_money[]" does not exist
Maybe the type in type is an issue!

There seems to be a problem related to casting of nested array of types in jOOQ 3.8. I've created an issue for this: https://github.com/jOOQ/jOOQ/issues/5571
The problem is that your custom type array type needs to be fully qualified when bound as a bind variable. If it isn't fully qualified, then the type isn't found by PostgreSQL. One possible workaround is described in this question: Permanently Set Postgresql Schema Path
You can add the shop schema on your user's search path:
ALTER ROLE <your_login_role> SET search_path TO shop;
... which means that elements inside of the shop schema no longer need to be fully qualified. Which can also be a bad thing, so be careful! :)

Related

Hibernate expected NUMBER got BINARY on null field

I can't understand why Hibernate is trying to set this field as a VARBINARY when it is null.
The Java data type is BigDecimal
The Oracle data type is Float
It is being set like this:
entityManager.createNamedQuery("blah").setParameter("target_field", object.sourceValue)
again - sourceValue is a BigDecimal, and when i debug it the value is null.
when it tries to execute this update, I get oracle error:
ORA-00932: inconsistent datatypes: expected NUMBER got BINARY
this is what shows up for this property in the console log:
o.h.type.descriptor.sql.BasicBinder : binding parameter [8] as [VARBINARY] - [null]
IF I do this silly hack in Java before I execute the update:
if (object.sourceValue == null) object.sourceValue = new java.math.BigDecimal(0);
Then it runs fine. So the cause of this error is definitely not anything else than hibernate doing something wrong when the field is null.
How do I fix this so I can set the field to null without hibernate mishandling it?
In the DB, the field is nullable.
It looks the issue has not been resolved yet even with newer version of hibernate 5.2.17.Final with JPA.
The null parameter is being sent as VARBINARY which causes the error
ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - ERROR: cannot cast type bytea to bigint
One workaround to this is to use the org.hibernate.Query that is wrapped into the org.hibernate.ejb.QueryImpl this way:
QueryImpl q = (QueryImpl) this.entityManager.createNamedQuery("myNamed.query");
q.getHibernateQuery().setParameter("name", value, org.hibernate.Hibernate.BIG_DECIMAL);
q.getResultList();
When value is null, hibernate still can know which type the parameter must be, so it doesn't translate the null to a binary value.
I have solved this problem by adding 2 annotations
#DynamicUpdate(value=true)
#DynamicInsert(value=true)
Also and most important, check mapping on your entity class for getters like (onetomany, manytoone).
All the best.

Hibernate: column does not exist

When I try to load an Entity with Hibernate I get the following error in postgres-log:
ERROR: column appuser0_.device_token does not exist at character 35
STATEMENT: select appuser0_.id as id1_27_0_, appuser0_.device_token as device_t2_27_0_,....
The column device_token definitely exists - and if I copy-paste the whole logged statement and execute it in PGAdmin, I get the expected result.
So what do I forget? What is the difference between the Hibernate statement and the manually executed one?
This issue was caused by the multi tenant configuration so that the wrong DataSource has been chosen.
Depending on how you defined the query, the problem might be located somewhere else: For example, HQL Queries are using the "property-names" of the class, not the column names.
And if you have something like:
#Column("device_token")
private String deviceToken;
Then your HQL-Query should target "deviceToken" and not "device_token". We also encountered a similar error once: Hibernate was reporting "user_id" is missing, because we named the property "userId" with the underscored version for the column name only.
This might be not the problem for you but worth double checking it.

Exception in thread "main" java.sql.SQLException: invalid name pattern: pakagename.sqlTypename

I am getting invalid name pattern exception when executing oracle function. which is return sql type as record which is present under package. If the type is present in Types directory it is working fine. But I am not able to execute if it is present in package directory. Please help me regarding this.
create or replace PACKAGE pkg_name
TYPE sqlTypeName
IS
RECORD(
firstVariable NUMBER,
secondVariable VARCHAR);
TYPE sqlTypeName_c is TABLE Of sqlTypeName INDEX BY pls_integer;
FUNCTION functionName() RETURN sqlTypeName_c
In java code:
Map typeMap = conn.getTypeMap();
typeMap.put("sqlTypeName_c", Array.class);
typeMap.put("sqlTypeName", Struct.class);
CallableStatement clstmt= null;
clstmt = conn.prepareCall("{ ? = call pkg_name.functionName() }");
clstmt.registerOutParameter(1, Types.ARRAY, "pkg_name.sqlTypeName_c");
clstmt.executeUpdate();
Array returnvalue = clstmt.getArray(1);
This will not work since the type is declared in the package. You will need to declare the sqlTypeName TYPE inside the schema.
Here are some of the questions which touch upon this issue -
Java- PLSQL- Call Table of records from java
Fetch Oracle table type from stored procedure using JDBC

Handling Hibernate's error codes?

Consider an hypothetical User table:
-- postgres, but it could have been any other SQL database
CREATE TABLE User(
id SERIAL PRIMARY KEY,
mail VARCHAR(32) UNIQUE NOT NULL
);
Let's assume I attempt to add two users with the same mail:
session.save(new User(1, "xpto#gmail.com"));
session.save(new User(2, "xpto#gmail.com"));
and execute it through Hibernate. Hibernate will throw me an ConstraintViolationException:
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:129)
...
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "users_mail_key"
Detail: Key (mail)=(xpto#gmail.com) already exists.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2198)
...
What I'd like to know is if there's some good way, other than having to manually parse the Exception's output text, to gather what is the reason of the error so I may correctly interpret and react to the problem.
I realize that this may actually be more of a Postgres Driver's problem than actually an Hibernate one, but I'm unsure at this stage so I thought it may opportune to ask in Hibernate's context.
So if you are able to get a value from getSQLState, you can handle the exception:
"All messages emitted by the PostgreSQL server are assigned five-character error codes that follow the SQL standard's conventions for "SQLSTATE" codes. Applications that need to know which error condition has occurred should usually test the error code, rather than looking at the textual error message."
From: http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html
23505 = unique_violation
Note: In this link there is also the list.
Well, after looking at Postgres Driver's source code it seems the problem lies with Postgres and not with Hibernate. PSQLException will contain some information, although it certainly isn't as polished as I first assumed :(
} catch (PSQLException e) {
ServerErrorMessage m = e.getServerErrorMessage();
System.out.println(m.getColumn());
System.out.println(m.getConstraint());
System.out.println(m.getDatatype());
System.out.println(m.getDetail());
System.out.println(m.getFile());
System.out.println(m.getHint());
System.out.println(m.getInternalPosition());
System.out.println(m.getInternalQuery());
System.out.println(m.getLine());
System.out.println(m.getMessage());
System.out.println(m.getPosition());
System.out.println(m.getRoutine());
System.out.println(m.getSchema());
System.out.println(m.getSeverity());
System.out.println(m.getSQLState());
System.out.println(m.getTable());
System.out.println(m.getWhere());
}
prints
null
users_mail_key
null
Key (mail)=(xpto#gmail.com) already exists.
nbtinsert.c
null
0
null
398
duplicate key value violates unique constraint "users_mail_key"
0
_bt_check_unique
public
ERROR
23505
users
null

PostgreSQL error, type Java_Object does not exist

I am using PostgreSQL and SQL PowerArchitect to design a physical datamodel. As I am using Hibernate which can directly persist Java objects in the database, I thought of using Java_Object as the type in SQL PowerArchitect. When I execute the query, I get this error. Kindly let me know if the type actually doesnt exist in PostgreSQL or I am making some mistake. Googling I am unable to find many reference to the data type.
CREATE TABLE public.Product_Info (
productinfoid VARCHAR NOT NULL DEFAULT nextval('public.product_info_productinfoid_seq'),
productbasic VARCHAR NOT NULL,
Product_Tags JAVA_OBJECT,
Product_Categories JAVA_OBJECT,
Product_Ship_Time JAVA_OBJECT,
CONSTRAINT productinfoid PRIMARY KEY (productinfoid, productbasic)
)
INFO 15-09 10:12:04,300 - sql statement failed: ERROR: type "java_object" does not exist
When creating tables you need to use column types that are supported by your database, like: CHAR, VARCHAR NUMERIC, BLOB, etc
Hibernate takes care of the the object type to database column type mapping.
You need FKs for these relationships:
Product_Tags,
Product_Categories
and probably a DATE/TIME/TIMESTAMP for this one:
Product_Ship_Time
The JAVA_OBJECT is for storing Java Class objects not Java Object instances!
Check org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings:
jdbcJavaClassMappings.put( Class.class, Types.JAVA_OBJECT );
So this type would be useful when fetching a Class object type from a database column (e.g. VARCHAR).

Categories