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

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....

Related

datatype mismatch SQLite: Android

It is giving me this error when I am adding JAVA string data to the TEXT data type is SQLite how to solve it?
public boolean addRow(Registration details) {
String userName = details.getName();
String userPassword = details.getPassword();
ContentValues values = new ContentValues();
values.put(KEY_ID, userName);
values.put(KEY_PASS, userPassword);
SQLiteDatabase db = this.getWritableDatabase();
long insert = db.insertOrThrow(TABLE_NAME, null, values);
return insert != -1;
}
E/SQLiteLog: (20) statement aborts at 5: [INSERT INTO UserDetails(id,password) VALUES (?,?)] datatype mismatch
In short you are probably trying to insert the userName into the wrong column. The id column is likely limited to integer values (long, int, short etc).
The only situations where you will get a datatype mismatch is if you are trying to insert a non integer value into the rowid column or an alias of the rowid column or if the column type is BOOLEAN and you try to insert a large BLOB. As you are not inserting a BLOB, then the former is the cause.
(20) SQLITE_MISMATCH
The SQLITE_MISMATCH error code indicates a datatype mismatch.
SQLite is normally very forgiving about mismatches between the type of a value and the declared type of the container in which that value is to be stored.
For example, SQLite allows the application to store a large BLOB in a column with a declared type of BOOLEAN. But in a few cases, SQLite is strict about types. The SQLITE_MISMATCH error is returned in those few cases when the types do not match.
The rowid of a table must be an integer. Attempt to set the rowid to anything other than an integer (or a NULL which will be automatically converted into the next available integer rowid) results in an SQLITE_MISMATCH error.
As such either the id (probably) or the password column is defined using INTEGER PRIMARY KEY (with or without AUTOINCRMENT or implied if the PRIMARY KEY clause is at the table level (after all the column definitions)).
At a guess, it may be that you have another column for the userName, if so then you may need to use values.put(????, userName); where ???? is the column into which the userName should be stored. This probably instead of values.put(KEY_ID, userName);
Not specifying a ContentValue for the id column will result in a unique number being generated and will be the value returned by the insertorThrow method.
Note that with insertOrThrow method you would never get -1 returned as an exception would be thrown. You perhaps want to just use the insert method, then -1 would be returned if the row could not be inserted.
when you use the insert method the underlying generated SQL is INSERT OR IGNORE .... which ignores the typical conflicts.
when you use the insertOrThrow method the underlying generated SQL is INSERT .... and results in an exception if a conflict occurs.
If you do want to provide the id, rather than have it generated, then you could also have values.put(KEY_ID, a_long_value); (where a_long_value is that (it could be int)), you would likely then change the signature of the method to enable the value to be passed.
Alternately, you may need to change the offending the column (probably the id column ) to be TEXT PRIMARY KEY instead of INTEGER PRIMARY KEY.
If you edit your question to show the SQL used to create the table a more specific answer can be provided.

Insert a new record with autogenerated id

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.

How to insert simple array into table field using mybatis annotation

My class field looks like
private int[] points = new int[]{1,1,1,1};
My innoDb table looks like
CREATE TABLE `test` (
`points` VARCHAR(70) NULL DEFAULT '0'
)
I try insert row into table thith this mapper (i'm using annotation)
#Insert({
"REPLACE INTO test (points)",
"values (#points},javaType=java.util.Array,typeHandler=org.apache.ibatis.type.ArrayTypeHandler)"
})
and getting the java.lang.IllegalStateException: No typehandler found for property points
How i can correctly insert this array in one field?
I could convert an array into a string, but I want to use the mybatis opportunity.
I see a typo in the snippet for Mybatis parameter, on curly braces {}:
VALUES ( {#points,javaType=java.util.Array,typeHandler=org.apache.ibatis.type.ArrayTypeHandler}
)
Furthermore, a custom ArrayTypeHandler implementation could be required, either for formatting as here it is stored as a String(Varchar), or if it is stored as SQL Array, then dependent on environment: DB Type/driver, pooled connection, application server ...

How do I insert a JPEG image into a PostgreSQL bytea field using Java and then display on a website using PHP?

I made a little Java program that reads image files (jpg/jpeg) and inserts them into a database table covers.cover, which is of type bytea.
I'm fairly certain that the byte[] cover parameter that is passed to the Java addCover(int riddim_id, byte[] cover, byte[] thumbnail) method contains valid jpeg data (I've tested by writing it to a .jpeg file, which displayed fine).
The database function add_cover(riddim INT, cover BYTEA, thumbnail BYTEA) appears to be called correctly; after a call there is data in the table and I do not get any SQL errors.
However, the data looks somewhat like
\377\330\377\340\000\020JFIF\000\001\001\001\001,\001,\000\000\377\333\000C\000\013\010\010\010\011\010\014\011\011\014\021\013\012\013\021\024\017\014\014\017\024\027\022\022\022\022\022\027\030\023\024\024\024\024\023\030\026\032\033\034\033\032\026""$$"".....0000000000\377\333\000C\001\014\014\014\020\020\020\027\021\021\027\030\024\023\024\030\036\033\034\034\033\036$\036\036\037\036\036$)# #)&($$$(&++))++000000000000000\377\300\000\021\010\005\200\005\230\003\001\021\000\002\021\001\003\021\001\377\304\000\033\000\000\003\000\003\001\001\000\000\000\000\000\000\000\000\000\000\000\001\002\003\004\006\005\007\377\304\000U\020\000\001\002\004\003\005\004\010\003\006\005\003\003\001\001\021\001\002\021\000!1A\003\022Q\004"2Ba\005\023bq\006CRr\201\221\241\3603\261\301\024#c\202\321
and so on, it doesn't look like valid data to me. I expected something more uniform (less special characters like ,, " and $), something like \x01E25A43.
When I go to http://foo.bar/image.php, which is the PHP script shown below, Firefox tells me that the image can not be displayed because it contains erroneous data.
I assume I am not correctly using the BYTEA field but I can't figure out what I am doing wrong. Any suggestions?
The database table looks like:
CREATE TABLE covers (
cover_id SERIAL PRIMARY KEY,
riddim_id SERIAL UNIQUE REFERENCES riddims (riddim_id),
coverhash VARCHAR(32) NOT NULL UNIQUE,
cover BYTEA NOT NULL,
thumbnail BYTEA NOT NULL
);
The database function that I am calling with java looks like:
CREATE OR REPLACE FUNCTION add_cover(_riddim_id INT, _cover BYTEA, _thumbnail BYTEA) RETURNS INTEGER AS $$
DECLARE
_cover_id INT;
BEGIN
SELECT cover_id INTO _cover_id FROM covers WHERE riddim_id = _riddim_id;
IF (_cover_id IS NULL) THEN
INSERT INTO covers (riddim_id, coverhash, cover, thumbnail) VALUES (
_riddim_id,
md5(_cover),
_cover,
_thumbnail
) RETURNING cover_id INTO _cover_id;
END IF;
RETURN _cover_id;
END;
$$ LANGUAGE plpgsql;
The Java method that calls the database function above using JDBC connector looks like:
private int addCover(int riddim_id, byte[] cover, byte[] thumbnail)
throws SQLException {
int cover_id;
try (CallableStatement cs = conn.prepareCall("{ ? = call add_cover(?, ?, ?) }")) {
cs.registerOutParameter(1, Types.INTEGER);
cs.setInt(2, riddim_id);
cs.setBytes(3, cover);
cs.setBytes(4, thumbnail);
cs.execute();
cover_id = cs.getInt(1);
}
return (cover_id != 0) ? cover_id : -1;
}
The PHP script (that I found somewhere on Google) that sends the data as image/jpeg to the browser:
<?php
// Connect to the database
$dbconn = pg_connect("<censored>");
// Get the bytea data
$res = pg_query("SELECT cover FROM covers WHERE cover_id = 11");
$raw = pg_fetch_result($res, 'cover');
// Convert to binary and send to the browser
header('Content-type: image/jpeg');
echo pg_unescape_bytea($raw);
?>
I'm running PostgreSQL version 9.4.4, using postgresql-9.4-1202.jdbc41.jar.
The query failed without giving an error message (more accurately the query always returned an empty resultset) because I was using pg_connect("host=foo.bar ...") but pg_hba.conf was not configured to allow external connections sent from localhost, apparently. Changing host=foo.bar to host=localhost solved the problem.
I'm not quite sure why $dbconn returned true, I figured out that something went wrong with the connection when checking /var/log/postgresql/postgresql-9.4-main.log which told me that the user I was trying to connect with had no valid settings for the specified host in pg_hba.conf.

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