解决springboot的JPA在Mysql8新增记录失败的问题
作者:程序员欣宸
springboot的JPA在Mysql8新增记录失败
springboot版本是1.3.0.M1,连接的mysql版本为8,用spring-boot-starter-data-jpa操作数据库,新增记录时应用抛出以下异常:
2018-02-21 12:52:59.471 DEBUG 6408 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet : Could not complete request org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:310) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:221) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:121) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy74.save(Unknown Source) at com.business.backend.dao.impl.BaseStockDaoImpl.save(BaseStockDaoImpl.java:24) at com.business.backend.controller.BaseStockController.savePost(BaseStockController.java:78) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:799) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:728) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:871) at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: org.hibernate.exception.GenericJDBCException: could not execute statement at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211) at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:96) at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:58) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3032) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3558) at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:98) at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:492) at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:197) at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:181) at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:216) at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:334) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:289) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:195) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:126) at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:344) at com.sun.proxy.$Proxy64.persist(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:291) at com.sun.proxy.$Proxy64.persist(Unknown Source) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:439) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:452) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:437) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:409) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ... 54 common frames omitted Caused by: java.sql.SQLException: Could not retrieve transation read-only status server at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:998) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:937) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:872) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:904) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:894) at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3613) at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3582) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2111) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2081) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2066) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208) ... 103 common frames omitted Caused by: java.sql.SQLException: Unknown system variable 'tx_read_only' at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:998) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3835) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3771) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2531) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2489) at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1446) at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3607) ... 108 common frames omitted
这一句是关键信息:Unknown system variable ‘tx_read_only',应该是mysql-connector-java的jar包的版本和数据库不匹配导致的;
当前的springboot项目是个maven工程,打开pom.xml文件看一下mysql-connector-java库的依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
如上所示,pom.xml中没有指定mysql-connector-java库的版本,我们只能用mvn命令查看默认版本是多少了,在pom.xml所在目录下执行以下命令,查看所有jar包的版本信息:
mvn dependency:tree
在输出的信息中看到了mysql-connector-java的版本是5.1.35,如下:
[INFO] +- mysql:mysql-connector-java:jar:5.1.35:compile
5.1.35版本偏高了,我们还是在pom.xml中指定一个低版本吧,修改mysql-connector-java库的依赖配置如下:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.15</version> </dependency>
如果您用的IDE是IntelliJ IDEA,修改上面的配置可能不会立即生效,请在pom.xml文件上点右键,选"Maven"->“Reimport”,如下图:
再次启动springboot应用并新增记录到mysql,这次一切顺利了。
spring data jpa + mysql 上踩过的坑
最近在做一个小项目,技术上用的是springboot+jpa(Java Persistence API)
spring data jpa让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,他是实现了orm思想的一套框架。
(1)先让实体类和数据库表对应,再让实体类属性和表里面字段对应
@Entity @Table(name = "t_user") public class User { @Id @GeneratedValue private Long id; private String nickname; private String username; private String password; getter and setter... }
(2)不需要直接操作数据库表,直接操作表对应的实体类对象
public interface UserRepository extends JpaRepository<User,Long> { User findByUsernameAndPassword(String username, String password); }
好了,问题就出在这第一步里的实体类属性和表里面字段对应
注意这个注解 @GeneratedValue ,他共有四种取值:(strategy = ***)
- AUTO主键由程序控制, 是默认选项 ,不设置就是这个
- IDENTITY 主键由数据库生成, 采用数据库自增长, Oracle不支持这种方式
- SEQUENCE 通过数据库的序列产生主键, MYSQL 不支持
- Table 提供特定的数据库产生主键, 该方式更有利于数据库的移植
以下是我今天解决问题过程中的一些想法,不一定对,仅供参考
在对应主键字段的属性上要是只标注了@GeneratedValue那就意味着把主键的生成策略交给了springboot,它将自动的判断你所使用的数据库,若是Mysql则设置成IDENTITY,Oracle则设置成SEQUENCE。
但我今天是用jpa自动生成的数据库
最上面举的实体类的例子也正是我编写实体类采用的注解方式,下面是我定义的实体类一共五个。
由于其中两个类之间的关系是多对多,所以按道理最终生成的表应该是六张,也就是五张实体类对应表和一张中间表。但实际情况确实这样的:
没错,他多了一张表!多了一张貌似相当于Oracle中的序列表(我也不太懂Oracle…就是觉得长的像)这张表只有一个字段,并且其他几张表的主键自增长都没有打开
就是指定了你下一个自动生成的主键的值,关键是所有表共用这个序列表,举个例子:
A表插入一条数据后自动生成的id=10,接下来你取B表插入一条数据,他生成的id就一定会接下去(=11)
如果你先在数据库中手动创建几条数据,让id的值超过序列表中的值
然后在项目系统中使用增加数据功能时就会…
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry ‘13' for key ‘PRIMARY'
破坏了完整性约束,在这里也就是说表中出现了重复的主键id=13,然后我不干任何事,在系统中再次执行我的添加功能:
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry ‘14' for key ‘PRIMARY'
那个序列表就会自增1然后再添加,发现id=14又已经存在了。相信大家已经看懂这序列表的机制了吧,就是说只要我在系统中在执行一次添加功能,这条数据就能够被加进数据库里了。
这样也太麻烦了吧,解决它其实很简单,只要在实体类中手动指定主键的生成策略,并且将表中对应主键字段的 自动递增打开 就行了,springboot就会放弃那张序列表而按照你指定的方式去生成主键了:
@Entity @Table(name = "t_user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String nickname; private String username; private String password;
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。