Why Hibernate schema-validation fail to validate schema - java

The same question has been asked number of times but none of them has solution to my problem.
I have created a hibernate + H2 + Sprinvg mvc project. I am using java based configuration. I have the following beans related to Datasource, SessionFactory and TransactionManager
#Configuration
#ComponentScan(basePackages="org.testpackage")
#EnableWebMvc
#EnableTransactionManagement
public class MyConfiguration extends WebMvcConfigurationSupport {
#Bean(initMethod="start",destroyMethod="stop")
public org.h2.tools.Server h2WebConsonleServer () throws SQLException {
return org.h2.tools.Server.createWebServer("-web","-webAllowOthers","-
webDaemon","-webPort", "8082");
}
#Bean
public DataSource getDataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(false)
.setName("mytestdb")
.setType(EmbeddedDatabaseType.H2)
.addDefaultScripts()
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.build();
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setHibernateProperties(hibernateProperties());
sessionFactory.setPackagesToScan(new String[] {"org.testpackage.model"});
return sessionFactory;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(final SessionFactory sessionFactory) {
final HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "validate");
hibernateProperties.setProperty("hibernate.show_sql", "true");
return hibernateProperties;
}
//Some more beans
}
I have the following Entity class
#Entity
#Table(name = "MYTESTDB.TEST_TABLE")
public class User{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "email", unique = true)
private String email;
public User(int id, String name, String email) {
super();
this.id = id;
this.name = name;
this.email = email;
}
public User() {
}
//Getters and Setters
}
in the DataSource bean I am using addDefaultScripts() and I have 2 sql scripts which create Schema in H2 and insert some predefined value. Which are as follows.
//schema.sql Script
CREATE SCHEMA `MYTESTDB` ;
Drop TABLE IF EXISTS MYTESTDBDB.TEST_TABLE;
CREATE TABLE MYTESTDB.TEST_TABLE (
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
EMAIL VARCHAR(20) NOT NULL,
);
CREATE UNIQUE INDEX ON MYTESTDB.TEST_TABLE (EMAIL)
//data.sql Script
INSERT INTO MYTESTDB.TEST_TABLE(id, name, email)
VALUES ('1', 'Tom', 'tom12#hotmail.com');
If I use hibernate hbm2ddl.auto property value "create" everything works fine, hibernate drops the table and recreate it. I have verified it from the web browser. But if I use "validate" property I get the following error exception
Error creating bean with name 'sessionFactory' defined in org.testPackage.configuration.MYConfiguration: Invocation of init method failed; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [MYTESTDB.TEST_TABLE]
Can anyone please help me to find out the problem?

Its working now. With the help of #Slaw I am able to use "Validate" property.
Use #Table(schema = "MYTESTDB", name = "TEST_TABLE") instead of #Table(name = "MYTESTDB.TEST_TABLE"). But I had to change a bit more in user entity class. Instead of #GeneratedValue annotation I added #GeneratedValue(strategy = GenerationType.IDENTITY). Now everything is working fine.
Thanks #Slaw and #Mykhailo for your valuable time.

Related

how to generate table to shema automatically from entity in spring boot

I have an Spring boot application with one entity. I generated an mysql schema and now I am trying to generate a table inside this when my spring boot application is starting I tried this with doing custom configuration classes but it doesnt generate any tables.
Configuration Class:
#Configuration
#PropertySource({"classpath:persistence-multiple-db.properties"})
#EnableJpaRepositories(
basePackages = "com.cbc.coorporateblinddateservice.repositories.meetinsSetting",
entityManagerFactoryRef = "meetingSettingEntityManager",
transactionManagerRef = "meetingSettingTransactionManager"
)
public class MeetingSettingsConfig {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean meetingSettingEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(meetingSettingDataSource());
em.setPackagesToScan(
"com.cbc.coorporateblinddateservice.entities.meetinsSetting");
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public DataSource meetingSettingDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("meetingSetting.jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("meetingSetting.jdbc.url"));
dataSource.setUsername(env.getProperty("meetingSetting.jdbc.user"));
dataSource.setPassword(env.getProperty("meetingSetting.jdbc.pass"));
dataSource.setSchema("coorporate_blinddate");
return dataSource;
}
#Primary
#Bean
public PlatformTransactionManager meetingSettingTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
meetingSettingEntityManager().getObject());
return transactionManager;
}
}
Entity:
#Entity
#Table(name = "MeetingsSetting")
public class MeetingsSetting {
#Id
#GeneratedValue
private Long id;
#Column(name = "meeting_name")
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
//
// #OneToMany(mappedBy = "meetingsSetting", cascade = CascadeType.ALL)
// private List<Date> meetingDate = new ArrayList<>();
}
And finally my config.property:
meetingSetting.jdbc.driverClassName=com.mysql.jdbc.Driver
meetingSetting.jdbc.url=jdbc:mysql://localhost:3306/Coorporate_Blinddate?createDatabaseIfNotExist=true&useSSL=true&serverTimezone=UTC
meetingSetting.jdbc.user=root
meetingSetting.jdbc.pass=
I know it takes my config.property because if I enter the wrong user it gives me an error. But know I want to generate the table directly from my entity class.
spring.jpa.hibernate.ddl-auto=create in application.properties file
this requires Spring JPA in your project

Unable to create unique key constraint in my project

My table(tbl_branch_type) exists in my database. Every other column is there, but I receive this error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/example/local/config/LocalDbConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Unable to create unique key constraint (code, bank_type_id) on table tbl_branch_type: database column 'bank_type_id' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
My BranchType entity is:
#Entity
#Table(
name = "tbl_branch_type",
uniqueConstraints = {
#UniqueConstraint(
name = "uc_branch_type_bank_id_branch_code",
columnNames = {"code", "bank_type_id"})
})
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Getter
#Setter
#EqualsAndHashCode(
callSuper = true,
exclude = {"bankType"})
#ToString(exclude = {"bankType"})
public class BranchType extends Auditing implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_branch_type")
#SequenceGenerator(sequenceName = "seq_branch_type", allocationSize = 1, name = "seq_branch_type")
private Long id;
#NotNull
#Size(min = 1, max = 20)
#Column(name = "code", length = 20, nullable = false)
private String code;
#NotNull
#Size(min = 1, max = 100)
#Column(name = "name", length = 100, nullable = false)
private String name;
#JsonIgnore
#ManyToOne
#JsonIgnoreProperties("")
private BankType bankType;
}
My LocalDbConfiguration class is:
#Configuration
#PropertySource({"classpath:application.yml"})
#EnableJpaRepositories(
basePackages = {"com.example.local.model.dao", "com.example.local.core.auth.repository"})
#EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class LocalDbConfiguration {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("dataSource") DataSource dataSource) {
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("database.platform", "org.hibernate.dialect.Oracle10gDialect");
return builder
.dataSource(dataSource)
.packages(
"com.example.local.model.entity",
"com.example.local.model.mapper",
"com.example.local.core.auth.domain")
.persistenceUnit("localPU")
.properties(properties)
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
#Bean
#Primary
#ConfigurationProperties("spring.datasource.hikari")
public HikariConfig defaultHikariConfig() {
return new HikariConfig();
}
#Bean
AuditorAware<Long> auditorProvider() {
return new AuditorProviderAware();
}
}
I believe this is the root cause of your problem: database column 'bank_type_id' not found. Try to create that column
I have solved my problem by adding this codes for entityManagerFactory in LocalDbConfiguration.class
properties.put(
"hibernate.physical_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
properties.put(
"hibernate.implicit_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");

Hibernate #ManyToOne do not update table

I have 2 entities - User and Role with mapping #ManyToOne. I want to change role in user, but Role want's to be updated too.
User entity:
#ManyToOne
#JoinColumn(name = "role_id", insertable = false, updatable = false)
private Role role;
role entity:
#OneToMany(mappedBy = "role")
private Set<User> users;
The error I get is:
java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.spring.model.Role
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:144)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1434)
My tables in DB are not set with CASCADE UPDATE or INSERT. I wasn't able to find appropriate solution. Thanks for your help
EDIT:
This is how I update User
public void update(User user) {
User entity = dao.findById(user.getId());
if(entity!=null) {
entity.setRole(user.getRole());
}
}
EDIT2:
My hibernate configuration
#Configuration
#EnableTransactionManagement
#ComponentScan({ "com.spring.configuration" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.spring.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
You should use the cascade attribute of the OneToMany annotation, it does not have any relationship with the database cascade operations (but can be affected by that).
#OneToMany(cascade = CascadeType.ALL, mappedBy="role", orphanRemoval = true)
private Set<User> users;
This way the operation will be propagated to the collection elements. You can check this answer for more info.
You can also modify the cascade property of the ManyToOne annotation.
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "role_id")
private Role role;

Table generated by hibernate but not showing in mysql database

Hi I am working on code of book called Master Spring MVC and I am using Mysql Database instead of embeded database. In book author have used xml based configuration but I am using java based. Till here everything is working fine.
The problem I am facing is that when I am running my code in tomcat server and with the logs (hibernate logs for table generation for particular table) everything is fine but the table is getting generated in my database but not this table called historic. I have attached my code along with log which shows that table is generated:
Please not this is a multi module project and therefore webapp, database is configured separately.
1) Hibernate Log to genrerate particular table
Hibernate log for class:
create table historic (
historic_type varchar(31) not null,
id integer not null auto_increment,
adj_close double precision,
change_percent double precision,
close double precision not null,
from_date datetime,
high double precision not null,
interval varchar(255),
low double precision not null,
open double precision not null,
to_date datetime,
volume double precision not null,
ask double precision,
bid double precision,
index_code varchar(255),
stock_code varchar(255),
primary key (id)
) engine=MyISAM
2)Database configuraton file
#Configuration
#ComponentScan("edu.zipcloud.cloudstreetmarket.core.entities")
#EnableTransactionManagement
public class JpaHibernateSpringConfig {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public DataSource dataSource() {
logger.info("============================[Data Source Configuration: Began]");
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost/cloudstreet");
ds.setUsername("root");
ds.setPassword("root");
logger.info("============================[Data Source Configuration: Ends]");
return ds;
}
#Bean
public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() {
logger.info("============================[LocalContainerEntityManagerFactoryBean: Began]");
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(dataSource());
bean.setPersistenceUnitName("jpaData");
bean.setPackagesToScan("edu.zipcloud.cloudstreetmarket.core");
bean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
bean.setJpaProperties(hibernateJPAProperties());
logger.info("===========================[LocalContainerEntityManagerFactoryBean: Ends]");
return bean;
}
public Properties hibernateJPAProperties() {
logger.info("============================[hibernateJPAProperties: Began]");
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.format_sql", "true");
properties.put("hibernate.hbm2ddl.auto", "create");
properties.put("hibernate.naming-strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("hibernate.default_schema", "public");
logger.info("============================[hibernateJPAProperties: Ends]");
return properties;
}
#Bean
public JpaTransactionManager jpaTransactionManager() {
logger.info("============================[jpaTransactionManager: Began]");
JpaTransactionManager manager = new JpaTransactionManager();
manager.setEntityManagerFactory(localContainerEntityManagerFactoryBean().getObject());
logger.info("============================[jpaTransactionManager: Ends]");
return manager;
}
}
2) DispatcherServlet:
public class DispatcherServletConifg extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { JpaHibernateSpringConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebServletInit.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
3) Historic entity
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "historic_type")
#Table(name = "historic")
public abstract class Historic implements Serializable {
private static final long serialVersionUID = -802306391915956578L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private double open;
private double high;
private double low;
private double close;
private double volume;
#Column(name = "adj_close")
private double adjClose;
#Column(name = "change_percent")
private double changePercent;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "from_date")
private Date fromDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "to_date")
private Date toDate;
#Enumerated(EnumType.STRING)
#Column(name = "interval")
private QuotesInterval interval;
Please take a look at logs and if you need any other information let me know.
Thanks.
I think problem is mysql keyword is being used as column name "interval"
try some other name
we can rename interval field like below
#Enumerated(EnumType.STRING)
#Column(name = "quotesinterval")
private QuotesInterval interval;
OR
any name which matches to your need.
Bottom line is should not use any mysql keywords
I tried that generated sql query as suggested by Ashish turned out that interval was not supported as field type so I changed my field name a little bit and it started working but still not sure if that wasen't supported. Configuration should have thrown a error
I changed #Enumerated(EnumType.STRING)
#Column(name = "interval")
private QuotesInterval interval;
to
#Enumerated(EnumType.STRING)
#Column(name = "'interval'") //<-- here
private QuotesInterval interval;
there is slight tick inside #column() that worked.
Thank you.
What worked for me... verify entity name.
Double-check that you're querying the correct table name in mysql. This happened to me, but I had been querying the class name, rather than the table name.
#Entity
#Table(name= "Account") // select * from Account, not AccountDetails
public class AccountDetails implements Serializable { /* class info */}

Hibernate cascade delete does not work

There are two entities: parent and child one.
#Entity
#Table(name = "university_group")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String name;
#OneToMany(mappedBy = "group", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Student> students = new HashSet<>();
// getters, setters, constructor, equals+hashcode ...
}
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String name;
private String password;
private String email;
#ManyToOne(optional = false)
private Group group;
// getters, setters, constructor, equals+hashcode ...
}
After removing group by em.remove(group) an exception is thrown:
javax.persistence.PersistenceException:
org.hibernate.exception.ConstraintViolationException ...
org.hibernate.engine.jdbc.spi.SqlExceptionHelper: ERROR:
UPDATE or DELETE in table "university_group" breaks foreign key constraint "fk_20su8ubuwt33je1a3ygal7wd6" of table "student"
It seems like hibernate is not deleting students before the group by means of Persistence Provider although it should. Of course, I am able to enable DB cascading, but I would better solve the problem.
Any ideas?
Configured the EntityManager by Spring configs
#Configuration
#EnableTransactionManagement
#PropertySource({"classpath:db.properties"})
public class PersistenceContext {
private static final String BASE_MODEL_SCAN_PACKAGE = "com.chiefhelpsystem.model";
#Value("${db.driverClassName}")
private String dbClassName;
#Value("${db.url}")
private String dbUrl;
#Value("${db.username}")
private String dbUserName;
#Value("${db.password}")
private String dbPassword;
#Bean
DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setMaxIdle(20);
ds.setMinIdle(0);
ds.setMaxActive(20);
ds.setDriverClassName(dbClassName);
ds.setUrl(dbUrl);
ds.setUsername(dbUserName);
ds.setPassword(dbPassword);
return ds;
}
#Bean
PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
#Bean(destroyMethod = "destroy")
LocalContainerEntityManagerFactoryBean emf() {
LocalContainerEntityManagerFactoryBean emFactory =
new LocalContainerEntityManagerFactoryBean();
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.POSTGRESQL);
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
emFactory.setDataSource(dataSource());
emFactory.setPackagesToScan(BASE_MODEL_SCAN_PACKAGE);
emFactory.setJpaVendorAdapter(jpaVendorAdapter);
emFactory.setJpaProperties(jpaProps());
emFactory.setPersistenceProvider(new HibernatePersistenceProvider());
return emFactory;
}
private Properties jpaProps() {
Properties properties = new Properties();
properties.setProperty("format_sql", "true");
return properties;
}
}
Hibernate 4.3.11, Spring 4.3.2
The problem was in the incorrect hachcode() method realization. As soon as I deleted it from sources, the "un-managed deleting" problem has appeared in hibernate TRACE logs and it should be further fixed.

Categories

Resources