Insert a new record with autogenerated id - java

I am trying to insert a new record into a simple database table with MyBatis but I get a strange exception. Mybe it is related to that I am not using POJO.
MyBatis version: 3.4.5
My table:
CREATE TABLE IF NOT EXISTS image
(
id BIGINT PRIMARY KEY,
content BYTEA
) WITHOUT OIDS;
MyBatis mapper:
#Insert("INSERT INTO image (id, content) VALUES (#{id}, #{content})")
#SelectKey(statement = "SELECT NEXTVAL('image_seq')", keyProperty = "id", before = true, resultType = long.class)
long insertImage(byte[] content);
The way I am trying to use it:
byte[] fileContent = IOUtils.toByteArray(inputStream);
long id = imageDao.insertImage(fileContent);
The exception what I get:
java.lang.ClassCastException: java.lang.Long cannot be cast to [B
at org.apache.ibatis.type.ByteArrayTypeHandler.setNonNullParameter(ByteArrayTypeHandler.java:26)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:53)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:87)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:93)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:64)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:86)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
I do not want to create POJO class with getter/setter method for this one "content" param but I think this issue is related to missing POJO.
What is the solution?
EDIT
I am trying to debug mybatis code and I have found "[B" in the parameterTypes:

#SelectKey is useful when you want to reuse generated value farther in the code, but it seems yo will not.
Then why not keep everything in the SQL:
INSERT INTO image (id, content) VALUES ((SELECT NEXTVAL('image_seq')), #{content})
For exception regarding parameters, parameters must be named with #Param annotation
int insertImage(#Param("content") byte[] content);
or
int insertImage(#Param("id) Long id, #Param("content") byte[] content)
Note that INSERT as well as UPDATE and DELETE statements returns type int being the number of inserted/updated/deleted rows, [...]
EDIT: unless you consider that under the hood, the java 8 PreparedStatement.executeLargeUpdate returning long is executed.
[...] and not the generated key as it is suggested. Then it seems you eventually want to get the key value, that means back to square one with #SelectKey and need for a POJO and a target property for the generated value. It even works with bulk insert with generated keys.
I have discovered lately that actual parameters name can be used (then your code will work as is) if following instructions in settings section of the documentation:
useActualParamName Allow referencing statement parameters by their
actual names declared in the method signature. To use this feature,
your project must be compiled in Java 8 with -parameters option.
(Since: 3.4.1) valid values: true | false default: true

java.lang.Long cannot be cast to [B
This is saying that you are trying to convert long to byte[]
Looking at source of org.apache.ibatis.type.ByteArrayTypeHandler:
public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType) throws SQLException {
ps.setBytes(i, parameter);
}
I think you need to remove {id} from insert annotation (as this value is autogenerated).
#Insert("INSERT INTO image (content) VALUES (#{content})")
Otherwise parameters are shifted by one.

Related

Oracle SQLInput not reading data, instead reads?

I have an issue related to SQLInput not reading data with readString()
Same code works on two different Oracle databases.
But on this one I have following issue:
This code:
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
userId = stream.readBigDecimal().longValue();
name = stream.readString();
modified = stream.readTimestamp();
}
returns userId and modified, but for name it returns "???" even though it has data in database for that name.
I have no idea what the issue is.
The data from query is return into oracle type similar to:
create or replace TYPE MY_ROW AS OBJECT (
USER_ID NUMBER,
name VARCHAR2(50),
MODIFIED TIMESTAMP
)
which is used in table type of that row type:
create or replace TYPE MY_TABLE as TABLE of MY_ROW
So I again, I have no idea why this doesn't work for that specific oracle database, when it works with two other ones.
It's like varchar2 fields with getString are not getting returned and I'm just getting ???
Anyone has any idea on what to do?
EDIT:
Even after including orai18n.jar in the classpath, the issue is still there
I finally managed to resolved it by combining orai18n.jar and ojdbc7.jar into same jar file.
Not sure why adding orai18n.jar separately in the class path didn't do the trick.

sql criteria with count function aliasToBean issue Hibernate

In my sql i have used count function like
COUNT( bip.seat_number) as totalSeats
Im trying to map this totalSeats attribute to a java bean called ScheduleSummery
Transformers.aliasToBean(ScheduleSummery.class)
Here is my java bean class
public class ScheduleSummery {
private String totalSeats;
public String getTotalSeats() {
return totalSeats;
}
public void setTotalSeats(String totalSeats) {
this.totalSeats = totalSeats;
}
}
Im getting below exception when trying to run this code. I have defined totalSeats as a String but it still shows expected type as java.lang.String
IllegalArgumentException occurred while calling setter for property
[ScheduleSummery.totalSeats
(expected type = java.lang.String)];
target = [ScheduleSummery#3f08662f], property value = [27]
any idea ? :(
The problem is your query returns some kind of number and your property is expecting a String.
You have to options
convert the result of the query to a String like so (should work with Oracle, the precise syntax for other databases might vary):
to_char(COUNT( bip.seat_number)) as totalSeats
change the property to the type you are getting out of the sql statement. You already tried int (according to other, now deleted answers. Other options you might try are: long, Long, Integer, BigInteger If try and error doesn't help, you can determine where in the code Hibernate tries the assignement (from the stacktrace), put a breakpoint there and see, what kind of value it actually has.

Select length(col) to get BLOB content size in jOOQ

The following SQL works
CREATE TABLE stored_file (
id INT AUTO_INCREMENT NOT NULL,
content BLOB,
content_length LONG,
PRIMARY KEY (id)
);
UPDATE stored_file SET content_length = length(content)
But I can't do the same in jOOQ.
getContext().update(STORED_FILE)
.set(STORED_FILE.CONTENT_LENGTH, DSL.length(STORED_FILE.CONTENT))
DSL.length only allows a String field type.
Is there a way around this?
Whenever you hit jOOQ's limits, resort to plain SQL. You can write your own length function like this:
class MyDSL {
public static Field<Long> length(Field<byte[]> field) {
return DSL.field("length({0})", Long.class, field);
}
}
And now use that in all your statements:
getContext().update(STORED_FILE)
.set(STORED_FILE.CONTENT_LENGTH, MyDSL.length(STORED_FILE.CONTENT))
Of course, if you really want to keep the length of a blob in sync with the blob itself, you're probably better off using a trigger (I'm assuming MySQL), or perhaps a view.
your code DSL.length(STORED_FILE.CONTENT) is generated exception because here allowed string but you are using BLOB data type so u changed your data type of the column content....

Hibernate query.list getting cast exception

I am trying to connect to sql server 2012. by hibernate.
sql query: SELECT top 3 P.,v. FROM TBLPOMASTER P join tblVendorMaster v on v.vendorid=p.VendorId where v.VendorCode=10001 and p.ApprovedStatus='Y'
I tried to translate HQL query as
List<TblPomaster> poMasterList = new ArrayList<TblPomaster>();
String sqlQuery = "from TblVendorMaster as v, TblPomaster as p where v.vendorId=p.vendorId and v.vendorCode=:vendorLoginId and p.approvedStatus='Y'";
Query query = HibernateUtil.getSession().createQuery(sqlQuery)
.setParameter("vendorLoginId", vendorLoginId);
query.setMaxResults(3);
poMasterList=query.list();
return poMasterList;
in the above code query is executing fine. But query.list() throwing RuntimeException as java.lang.String cannot be cast to java.lang.Long
What is the solution for the above error
You can change your attribute's type from String to Long
You can cast the value to long:
Long.valueOf(String s).longValue();
It is also recommended to get the results of your query with:
query.getResultList();
Looks like you are retrieving a value from your database and hibernate is trying to convert that to a Long value.
Perhaps you have mapped this table column to an erroneously typed attribute in your model class. I'd check which text values are defined a 'numeric' within your class.
--
#theMarceloR: vendorCode datatype is Long in model file. But I am sending vendorLoginId a string value. is this the problem?
Change the attribute in the model config from Long to String and see what happens.
SOLUTION:
Sagar updated his code and now he's sending vendorCode as a parameter, the value is casted from string to long.

java web service working with PostgreSQL database

my code is written in java, and I am really new to java, so i hope my explanations are correct:
i have a java written web service that works with a data base.
the data base types can be PostgreSQL and mysql.
so my webservice works with the JDBC connection for both data bases.
one of my data base tables is table urls,
for postgressql it is created like this:
CREATE TABLE urls (
id serial NOT NULL primary key,
url text not null,
type integer not null);
for mysql it is creates like this:
CREATE TABLE IF NOT EXISTS URLS (
id INTEGER primary key NOT NULL AUTO_INCREMENT,
url varchar (1600) NOT NULL,
type INTEGER NOT NULL );
when I try inserting data to this table I use an entity called urls:
this entity has:
private BigDecimal id;
private String url;
private BigInteger type;
when I try to insert values to the urls table I assign values to the urls entity, while leaving the id as NULL since it is AUTO_INCREMENT for mysql and serial for postgres.
the query works for my sql, but fails for postgress.
in the postgres server log I can see the following error:
null value in column "id" violates not-null constraint
cause I sends NULL as the id value.
I found out that in order for the query to work I should use this query:
INSERT INTO URLS(ID, TYPE, URL) VALUES(DEFAULT, 1, 'DED'); or this one:
INSERT INTO URLS(TYPE, URL) VALUES(1, 'DED'); or this one:
instead of this one, that I use:
INSERT INTO URLS(ID, TYPE, URL) VALUES(NULL, 1, 'DED');
so my question is,
how do I assign the DEFAULT value to a BigDecimal value in java ?
is removing the id from my entity is the right way to go ?
how can I make sure that any changes I do to my code wont harm the mysql or any other data base that I will use ?
If you specify the column name in the insert query then postgres does not take the default value. So you should use your second insert query.
INSERT INTO URLS(TYPE, URL) VALUES(1, 'DED');
This syntax is correct for both postgres and MySQL.
This should resolve your question (1) and (3). For (2) DO NOT delete the id field from your entity. This id is going to be your link to the database row for a specific object of the entity.
1 - I think it is proper to use Long or long types instead of BigDecimal for id fields.
2 - Yes it generally helps, but it lowers portability. BTW, using an ORM framework like Hibernate may be a good choice.
3 - Integration testing usually helps and you may want to adopt TDD style development.
When using this statement:
INSERT INTO URLS(ID, TYPE, URL) VALUES(NULL, 1, 'DED');
you are telling the database that you want to insert a NULL value into the column ID and Postgres will do just that. Unlike MySQL, PostgreSQL will never implicitely replace a value that you supply with something totally different (actually all DBMS except MySQL work that way - unless there is some trigger involved of course).
So the only solution to is to actually use an INSERT that does not supply a value for the ID column. That should work on MySQL as well.

Categories