Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27535

MySQLSyntaxErrorException while attempting to persist blob with hibernate

I'm creating some simple webpage with Spring MVC, Hibernate4 and MySQL5. One of models I use contains BLOB value (byte[]). When I try to persist that model with entityManager.persist() I get MySQLSytnaxException. Actually there are more problems with current configuration, such as persist/merge/remove ignoring @Transactional annotation, but this one is the most critical.

I already tried using session.save(object); or replacing byte[] by Blob. Still the same result. All working examples I found use completely diffirent approach - e.g. they use HibernateSessionManager and HibernateTransactionManager instead of JPA ones - and I would like to found solution that doesn't require complete change in how I persist entities, when I'm still not sure that it would help.

Could You tell me what mistakes I made in code/configuration/assumptions?

Beginning of a stack trace together with Hibernate trace:

Hibernate: 
        insert 
        into
            updates
            (changelog, added, developmentVersion, filedata, filedataType, major, minor, nightly, release, package, type, uploader, id) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
22:53:10,888 TRACE BasicBinder:83 - binding parameter [1] as [VARCHAR] - sdfsd
22:53:10,891 TRACE BasicBinder:71 - binding parameter [2] as [DATE] - <null>
22:53:10,894 TRACE BasicBinder:83 - binding parameter [3] as [BOOLEAN] - false
22:53:10,898 TRACE BasicBinder:83 - binding parameter [4] as [BLOB] - javax.sql.rowset.serial.SerialBlob@298fd36c
22:53:10,924 TRACE BasicBinder:83 - binding parameter [5] as [VARCHAR] - image/png
22:53:10,926 TRACE BasicBinder:83 - binding parameter [6] as [INTEGER] - 1
22:53:10,928 TRACE BasicBinder:83 - binding parameter [7] as [INTEGER] - 0
22:53:10,935 TRACE BasicBinder:83 - binding parameter [8] as [INTEGER] - 0
22:53:10,936 TRACE BasicBinder:83 - binding parameter [9] as [INTEGER] - 0
22:53:10,939 TRACE BasicBinder:83 - binding parameter [10] as [INTEGER] - 36
22:53:10,941 TRACE EnumType:292 - Binding {0} to parameter: {1}
22:53:10,944 TRACE BasicBinder:83 - binding parameter [12] as [INTEGER] - 18
22:53:10,955 TRACE BasicBinder:83 - binding parameter [13] as [INTEGER] - 0
22:53:10,998  WARN SqlExceptionHelper:143 - SQL Error: 1064, SQLState: 42000
22:53:10,999 ERROR SqlExceptionHelper:144 - 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 'release, package, type, uploader, id) values ('sdfsd', null, 0, _binary'‰PNG
' at line 1
22:53:11,027  INFO AbstractBatchImpl:195 - HHH000010: On release of batch it still contained JDBC statements
Nov 07, 2012 10:53:11 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [repoApplication] in context with path [/server] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: 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 'release, package, type, uploader, id) values ('sdfsd', null, 0, _binary'‰PNG
' at line 1; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: 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 'release, package, type, uploader, id) values ('sdfsd', null, 0, _binary'‰PNG
' at line 1] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 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 'release, package, type, uploader, id) values ('sdfsd', null, 0, _binary'‰PNG
' at line 1

beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    ...
    >

    ...

    <!-- Hibernate configuration -->    

    <!-- Specifies dataSource object managing connections to database -->  
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
        <property name="driverClassName" value="${database.driver}" /> 
        <property name="url" value="${database.url}" /> 
        <property name="username" value="${database.user}" /> 
        <property name="password" value="${database.password}" />
    </bean>

    <!-- Defines SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        ...
        <property name="hibernateProperties">
            <util:properties location="classpath:Hibernate.properties" />
        </property>
    </bean>

    <!-- Defines TransactionManager -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- Binds TransactionManager to annotations -->
    <tx:annotation-driven transaction-manager="transactionManager" />

    <!-- Enables Spring annotations -->
    <context:annotation-config />

    ...

</beans>

Hibernate.properties:

hibernate.database         =MYSQL
hibernate.dialect          =org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql         =true
hibernate.format_sql       =true
hibernate.use_sql_comments =true
hibernate.hbm2ddl.auto     =update

All tables in MySQL database uses InnoDB engine.

Update.java (model):

import java.sql.Blob;
import java.sql.Date;
import java.sql.SQLException;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.sql.rowset.serial.SerialBlob;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.Type;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

...

@Entity
@Table(name = "updates")
@VersionNumberCorrect
public class Update {
    @Id
    @Column(name = "id", unique = true)
    private int id;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "package")
    private Package thePackage;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "uploader")
    private User uploader;

    @Column(name = "added")
    private Date date;

    @Column(name = "changelog")
    @NotNull
    @NotEmpty
    private String changelog;

    @Column(name = "major")
    private int major;

    @Column(name = "minor")
    private int minor;

    @Column(name = "release")
    private int release;

    @Column(name = "nightly")
    private int nightly;

    @Column(name = "developmentVersion")
    private boolean developmentVersion;

    @Column(name = "type")
    @Enumerated(EnumType.ORDINAL)
    private EUpdateStrategy type;

    @Column(name = "filedata")
    @Lob
    @Type(type = "blob")
    @NotNull
    private Blob filedata;

    @Column(name = "filedataType")
    private String filedataType;

    public Update() {
    }

    ...
}

UpdateServiceImp.java (Service):

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

...

@Service
@Transactional
public class UpdateServiceImp implements UpdateService {
    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public void persist(Update update) {
        getSession().persist(update);
    }

    @Override
    public Update merge(Update update) {
        return (Update) getSession().merge(update);
    }

    @Override
    public void remove(Update update) {
        getSession().delete(update);
    }

    ...

    /**
     * Returns new Session instance.
     * 
     * @return new Session
     */
    private Session getSession() {
        return sessionFactory.getCurrentSession();
    }
}

EDIT: I changed EntityManagerManager usage into Hibernate's SessionFactory - I tried to change that thinking it might help. It didn't, but code got a little bit cleaner :). I've added some information from Hibernate log that might be helpful. I've also added Hibernate.properties content, since this error is most likely something Hibernate configuration related.

Upvotes: 1

Views: 3186

Answers (1)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27535

Thanks to help of a fellow developer I found out what was wrong: I was using release as a name of one of the properties. I found out that release is a keyword in MySQL, and when it appears in query it makes syntax invalid.

I was honestly surprised to learn that Hibernate does NOT use apostrophes with columns', schemas' and tables' names. I thought it was a common practice to write SQL this way:

INSERT INTO `mySqlTable` VALUES (null, 'value') ;

or

INSERT INTO 'dbo'.'msSqlTable' VALUES (null, 'value');

but Hibernate would do it this way instead:

INSERT INTO mySqlTable VALUES (null, 'value') ;

For MySQL5 the list of reserved words is quite long: http://dev.mysql.com/doc/refman/5.0/en/reserved-words.html

I guess I will have to be more careful with choosing columns' names in the future. Hope that will help someone.

Upvotes: 6

Related Questions