1

I am getting a configuration stacktrace error when I try to load my spring boot application with a Hibernate 4 - MySQL configuration. I can't figure out what is causing this foreign key constraint problem. Any help greatly appreciated!

**Caused by: java.sql.SQLException: Cannot add foreign key constraint
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:963)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3966)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3902)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2526)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2673)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2503)
at com.mysql.jdbc.StatementImpl.executeInternal(StatementImpl.java:839)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:739)
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:49)
...**

Here are my three database tables:

enter image description here

Principal Entity

@Entity
public class Principal implements {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long principal_id;

@Column(name = "username", unique = true, nullable = false)
private String name;

@Column(unique = true, nullable = false)
private String email;

@Column(nullable = false)
private String password;

@Column(nullable = false)
private Boolean locked;

@ManyToMany( fetch = FetchType.EAGER)
@JoinTable(joinColumns = { @JoinColumn(name = "PRINCIPAL_ID", referencedColumnName = "PRINCIPAL_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID", referencedColumnName = "ROLE_ID") })
private Set<Role> roles;


public Principal() {
    super();

    locked = false;
}

public Principal(final String nameToSet, final String passwordToSet, final Set<Role> rolesToSet) {
    super();

    name = nameToSet;
    password = passwordToSet;
    roles = rolesToSet;
}

public Principal(final UserDto userDto) {
    super();

    name = userDto.getName();
    password = userDto.getPassword();
    roles = userDto.getRoles();
}

@Override
public Long getId() {
    return principal_id;
}

@Override
public void setId(final Long idToSet) {
    principal_id = idToSet;
}

@Override
public String getName() {
    return name;
}

public void setName(final String nameToSet) {
    name = nameToSet;
}

public String getEmail() {
    return email;
}

public void setEmail(final String emailToSet) {
    email = emailToSet;
}

public String getPassword() {
    return password;
}

public void setPassword(final String passwordToSet) {
    password = passwordToSet;
}

public Set<Role> getRoles() {
    return roles;
}

public void setRoles(final Set<Role> rolesToSet) {
    roles = rolesToSet;
}

public Boolean getLocked() {
    return locked;
}

public void setLocked(final Boolean lockedToSet) {
    locked = lockedToSet;
}

}

Role Entity

@Entity
public class Role implements INameableEntity, INameableDto {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long role_id;

@Column(unique = true, nullable = false)
@Size(min = 2, max = 30)
@NotNull
private String name;

public Role() {
    super();
}

public Role(final String nameToSet) {
    super();
    name = nameToSet;
}

// API

@Override
public Long getId() {
    return role_id;
}

@Override
public void setId(final Long idToSet) {
    role_id = idToSet;
}

@Override
public String getName() {
    return name;
}

public void setName(final String nameToSet) {
    name = nameToSet;
}

persistence-mysql.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/qlc?&useSSL=false
jdbc.username=root
jdbc.password=password


hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update

jpa.generateDdl=true

UmPersistenceJpaConfig.java

@Configuration
@EnableTransactionManagement
@ComponentScan({ "org.qlc.um.persistence" })
@PropertySource({ "persistence-mysql.properties" })
@EnableJpaRepositories(basePackages = "org.qlc.um.persistence.dao")
public class UmPersistenceJpaConfig {

@Autowired
private Environment env;

public UmPersistenceJpaConfig() {
    super();
}

// beans

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setPackagesToScan(new String[] { "org.qlc.um" });
    final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(vendorAdapter);
    em.setJpaProperties(additionalProperties());
    return em;
}

@Bean
public DataSource dataSource() {
    final DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
    dataSource.setUrl(env.getProperty("jdbc.url"));
    dataSource.setUsername(env.getProperty("jdbc.username"));
    dataSource.setPassword(env.getProperty("jdbc.password"));
    return dataSource;
}

@Bean
public JpaTransactionManager transactionManager() {
    final JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
    return transactionManager;
}

@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
    return new PersistenceExceptionTranslationPostProcessor();
}

//

final Properties additionalProperties() {
    final Properties hibernateProperties = new Properties();
    hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto", "create-drop"));
    hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));

    // setProperty("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
    // setProperty("hibernate.ejb.naming_strategy", org.hibernate.cfg.ImprovedNamingStrategy.class.getName());
    return hibernateProperties;
}

}
John Stafford
  • 565
  • 2
  • 9
  • 29
  • After getting the error run a `SHOW ENGINE INNODB STATUS` statement in mysql directly and it will tell you what exactly went wrong with the foreign key creation. – Shadow Nov 10 '16 at 23:40
  • this was an error that I got when doing a "clean install" on my maven configuration for my application. I think this is an application configuration problem of hibernate not talking to my mysql correctly. The problem is not in the mysql table. – John Stafford Nov 10 '16 at 23:46
  • But did you SHOW ENGINE INNODB STATUS as suggested by @shadow? – e4c5 Nov 10 '16 at 23:53
  • SHOW ENGINE INNODB STATUS = foreign key (principal_id) references Principal (principal_id): Cannot find an index in the referenced table where the referenced columns appear as the first columns, or column types in the table and the referenced table do not match for constraint.Note that the internal storage type of ENUM and SET changed in tables created with >= InnoDB-4.1.12, and such columns in old tables cannot be referenced by such columns in new tables. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-foreign-key-constraints.html for correct foreign key definition. – John Stafford Nov 11 '16 at 00:09
  • not sure what the problem is? it looks like the foreign constraint of my principal_id on the principal table to the principals_roles table is the problem. Is it my use of the Set in my Java entity for principal? – John Stafford Nov 11 '16 at 00:10
  • Since this is @ManyToMany, the principals_users table bridges the gap between the principal and role tables. It looks like it is complaining about the 'fk_principal' constraint of the column principal_id in the principals_roles table referencing the principal_id in the principal table. Not sure why it is complaining, even with the SHOW ENGINE INNODB STATUS message. – John Stafford Nov 11 '16 at 00:24
  • how are you saving, can you show the code? – kuhajeyan Nov 11 '16 at 06:40
  • @John Stafford, Did you manage to get the code working? I am facing the same problem. Any help is much appreciated. – Vijay Kalidindi Jan 24 '17 at 06:13

1 Answers1

0

my guess, since you have not marked the @column in id of entity it is taking default property name of your getter which will be 'id'

try, Principal

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Column(name="principal_id")
private Long principal_id;

Role

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Column(name="role_id")
private Long role_id;
kuhajeyan
  • 10,727
  • 10
  • 46
  • 71