启动Spring boot报错:nested exception is java.sql.SQLException: Field 'id' doesn't have a default value

时间:2023-03-08 21:49:39

  先看具体日志:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter ; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement # of URL [jar:file:/home/wlf/springboot-0.0.-SNAPSHOT.jar!/BOOT-INF/classes!/schema.sql]: insert into t_user(user_name, pass_word, role_value) select 'admin', '', 'Admin' from dual where not exists(select user_name, pass_word, role_value from t_user where user_name='admin' and pass_word='' and role_value='Admin'); nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:) ~[spring-context-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:) ~[spring-context-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:) ~[spring-context-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:) ~[spring-boot-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:) ~[spring-boot-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:) ~[spring-boot-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:) ~[spring-boot-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:) ~[spring-boot-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:) ~[spring-boot-2.1..RELEASE.jar!/:2.1..RELEASE]
at com.crocodile.springboot.SpringbootApplication.main(SpringbootApplication.java:) ~[classes!/:0.0.-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:) ~[na:na]
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:) ~[springboot-0.0.-SNAPSHOT.jar:0.0.-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:) ~[springboot-0.0.-SNAPSHOT.jar:0.0.-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:) ~[springboot-0.0.-SNAPSHOT.jar:0.0.-SNAPSHOT]
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:) ~[springboot-0.0.-SNAPSHOT.jar:0.0.-SNAPSHOT]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement # of URL [jar:file:/home/wlf/springboot-0.0.-SNAPSHOT.jar!/BOOT-INF/classes!/schema.sql]: insert into t_user(user_name, pass_word, role_value) select 'admin', '', 'Admin' from dual where not exists(select user_name, pass_word, role_value from t_user where user_name='admin' and pass_word='' and role_value='Admin'); nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
... common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement # of URL [jar:file:/home/wlf/springboot-0.0.-SNAPSHOT.jar!/BOOT-INF/classes!/schema.sql]: insert into t_user(user_name, pass_word, role_value) select 'admin', '', 'Admin' from dual where not exists(select user_name, pass_word, role_value from t_user where user_name='admin' and pass_word='' and role_value='Admin'); nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.postProcessAfterInitialization(DataSourceInitializerPostProcessor.java:) ~[spring-boot-autoconfigure-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
... common frames omitted
Caused by: org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement # of URL [jar:file:/home/wlf/springboot-0.0.-SNAPSHOT.jar!/BOOT-INF/classes!/schema.sql]: insert into t_user(user_name, pass_word, role_value) select 'admin', '123456, 'Admin' from dual where not exists(select user_name, pass_word, role_value from t_user where user_name='admin' and pass_word='' and role_value='Admin'); nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:) ~[spring-jdbc-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:) ~[spring-jdbc-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:) ~[spring-jdbc-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.runScripts(DataSourceInitializer.java:) ~[spring-boot-autoconfigure-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.createSchema(DataSourceInitializer.java:) ~[spring-boot-autoconfigure-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker.afterPropertiesSet(DataSourceInitializerInvoker.java:) ~[spring-boot-autoconfigure-2.1..RELEASE.jar!/:2.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:) ~[spring-beans-5.1..RELEASE.jar!/:5.1..RELEASE]
... common frames omitted
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:) ~[mysql-connector-java-8.0..jar!/:8.0.]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:) ~[mysql-connector-java-8.0..jar!/:8.0.]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:) ~[mysql-connector-java-8.0..jar!/:8.0.]
at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:) ~[mysql-connector-java-8.0..jar!/:8.0.]
at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:) ~[mysql-connector-java-8.0..jar!/:8.0.]
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:) ~[HikariCP-3.2..jar!/:na]
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~[HikariCP-3.2..jar!/:na]
at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:) ~[spring-jdbc-5.1..RELEASE.jar!/:5.1..RELEASE]
... common frames omitted

  从上述日志中看到,启动Spring boot时先去初始化Spring data JPA,但没有加载到数据源DataSource对象。那为啥数据源会加载失败,其实引发的原因是执行初始化脚本执行失败。先看application.properties文件配置的数据源:

#mysql数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.initialization-mode=always

  最后一行指定了数据源初始化时必须去执行我的预置脚本schema.sql:

create table if not exists t_user (
id bigint not null auto_increment,
user_name varchar() not null,
pass_word varchar() not null,
role_value varchar() not null,
primary key (id)
); insert into t_user(user_name, pass_word, role_value) select 'admin', '', 'admin' from dual where not exists(select user_name, pass_word, role_value from t_user where user_name='admin' and pass_word='' and role_value='admin');

  重启Spring boot时为防止重复建表用了if not exists、重复插入用了insert into...select from dual where not exists,都是先看有没有,没有了再操作。因为这里的id是自增长的,所以就算我的插入语句不指定id,mysql也会默认自动给id赋一个自增长的值。如果真按以上执行了,那应该不会出现问题才对。但事实很明显,Spring boot不按此套路来。看下我的实体类中主键ID的定义:

@Entity
@Table(name = "T_USER")
public class User implements Serializable {
@Id
@GeneratedValue
   private Long id; private String userName; private String passWord; private String roleValue; ...
}

  以上实例类中我们指定ID是自增长的,可是这个增长不是mysql的自增,而是Spring Data JPA去设置的。我们看下@GeneratedValue究竟给我们设置了怎样的生成策略:

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GeneratedValue {
GenerationType strategy() default GenerationType.AUTO; String generator() default "";
}

  它默认是GenerationType.AUTO,若想让mysql接手设置自动增长,必须用这个:

   @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

  生成类型有四种:

  • TABLE:使用一个特定的数据库表来保存主键,也就是你可以自己指定哪张表。
  • SEQUENCE:根据底层数据库的序列来生成主键,当然条件是得数据库支持序列的生成。
  • IDENTITY:主键由数据库自动生成(主要是自动增长型),比如mysql就会设置扩展属性为auto_increment
  • AUTO:主键由程序控制,程序自动生成表,比如在mysql里会自动创建表hibernate_sequence,每次新增都会读取next_val值作为自增长ID。

  我们看下使用AUTO生成的表结构:

mysql> desc t_user;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | bigint() | NO | PRI | NULL | |
| pass_word | varchar() | YES | | NULL | |
| role_value | varchar() | YES | | NULL | |
| user_name | varchar() | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
rows in set (0.00 sec)

  直接insert不带主键默认值必然报错:

mysql> insert into t_user(user_name, pass_word, role_value) values ('wlf', '', 'sales');
ERROR (HY000): Field 'id' doesn't have a default value

  换IDENTITY试试:

mysql> desc t_user;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint() | NO | PRI | NULL | auto_increment |
| user_name | varchar() | NO | | NULL | |
| pass_word | varchar() | NO | | NULL | |
| role_value | varchar() | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
rows in set (0.01 sec)

  我们看到mysql的自增长属性带上了,这时再使用不带自增长的id字段入库成功了:

mysql> insert into t_user(user_name, pass_word, role_value) values('aaa', '', 'sales');
Query OK, row affected (0.02 sec)

  另外需要注意,mysql里没有建表前你指定主键生成策略为IDENTITY会加上自增长属性,但如果你开始是AUTO的,后来再改为IDENTITY的,那么你需要手动去mysql里修改约束,自己加上自增长属性,或者drop表重来。