I am currently developing various junit tests for hibernate. Some Hibernate model classes refer to a different schema as "public". For example, the schema "internal". Now I have to create this schema "internally" before the tables are created. How do I get this implemented with Hibernate?
Test.java
#Before
public void setUp() throws IOException, URISyntaxException, InterruptedException {
Configuration configuration = new Configuration();
configuration.addAnnotatedClass(Table.class);
configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
configuration.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
configuration.setProperty("hibernate.connection.url", "jdbc:h2:mem:test");
configuration.setProperty("hibernate.hbm2ddl.auto", "create");
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
this.sf = configuration.buildSessionFactory(serviceRegistry);
this.injector = Guice.createInjector(new CoreModule());
this.injector.injectMembers(this);
Key<SimpleScope> simpleScopeKey = Key.get(SimpleScope.class, Names.named("scriptingScope"));
this.scope = this.injector.getInstance(simpleScopeKey);
}
#After
public void tearDown() throws Exception {
this.sf.close();
}
Table.java
#Entity
#Table(schema = "internal", name = "table")
public class Table {
#Id
private Long id;
#Column(name = "name")
private String name;
}
Error
Schema "RIS" not found; SQL statement:
create table internal.table (id bigint not null, name varchar(255), primary key (id)) [90079-193]
Feb 13, 2017 10:52:20 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Solution for this problem can be found here: https://touk.pl/blog/2011/01/18/hibernate-hbm2ddl-wont-create-schema-before-creating-tables/
Related
I have application-test.properties where I defined two datasources
app.datasource.server.url=jdbc:h2:mem:fooserver
app.datasource.server.username=sa
app.datasource.server.password=
app.datasource.server.driverClassName=org.h2.Driver
app.datasource.server.hikari.minimumIdle=5
app.datasource.server.hikari.maximumPoolSize=50
app.datasource.server.hikari.idleTimeout=50000
app.datasource.server.hikari.maxLifetime=55000
app.datasource.manager.url=jdbc:h2:mem:barmanager
app.datasource.manager.username=sa
app.datasource.manager.password=
app.datasource.manager.driverClassName=org.h2.Driver
app.datasource.manager.hikari.minimumIdle=5
app.datasource.manager.hikari.maximumPoolSize=50
app.datasource.manager.hikari.idleTimeout=50000
app.datasource.manager.hikari.maxLifetime=55000
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=create-drop
#logging
logging.level.root=info
logging.file=foobar-rest-test.log
#required for SpringBootTest does not know why
spring.main.allow-bean-definition-overriding=true
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Each of the datasource requires schema to be available named "foo" this will be created by a schema-fooserver.sql and schema-barmanager.sql in each of these sql scripts the foo schema will be created. Therefore I defined a dataSourceIntializer Bean where I can define which schema-sql file will be loaded.
#Bean(name = "managerDataSourceInitializer")
public DataSourceInitializer dataSourceInitializer1(#Qualifier("managerDataSource") DataSource datasource) {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(new ClassPathResource("schema-barmanager.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(datasource);
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
return dataSourceInitializer;
}
#Bean(name = "serverDataSourceInitializer")
public DataSourceInitializer dataSourceInitializer1(#Qualifier("serverDataSource") DataSource datasource) {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(new ClassPathResource("schema-fooserver.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(datasource);
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
return dataSourceInitializer;
}
During the start of my test the logs show that these schema files have been called and executed.
2019-03-28 15:04:34.252 DEBUG 3124 --- [main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from class path resource [schema-fooserver.sql]
2019-03-28 15:04:34.252 DEBUG 3124 --- [main] o.s.jdbc.datasource.init.ScriptUtils : 0 returned as update count for SQL: CREATE SCHEMA IF NOT EXISTS FOO
2019-03-28 15:04:34.252 DEBUG 3124 --- [main] o.s.jdbc.datasource.init.ScriptUtils : Executed SQL script from class path resource [schema-fooserver.sql] in 0 ms.
2019-03-28 15:04:34.252 DEBUG 3124 --- [main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from class path resource [schema-barserver.sql]
2019-03-28 15:04:34.252 DEBUG 3124 --- [main] o.s.jdbc.datasource.init.ScriptUtils : 0 returned as update count for SQL: CREATE SCHEMA IF NOT EXISTS FOO
2019-03-28 15:04:34.252 DEBUG 3124 --- [main] o.s.jdbc.datasource.init.ScriptUtils : Executed SQL script from class path resource [schema-barserver.sql] in 0 ms.
Now when I try to execute my test case it fails because of the following error:
28 15:04:36.035 DEBUG 3124 --- [ main] org.hibernate.SQL : insert into foo.Account (uid, password_hash, login) values (null, ?, ?)
Hibernate: insert into foo.Account (uid, password_hash, login) values (null, ?, ?)
2019-03-28 15:04:36.036 DEBUG 3124 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : could not prepare statement [insert into foo.Account (uid, password_hash, login) values (null, ?, ?)]
org.h2.jdbc.JdbcSQLException: Tabelle "ACCOUNT" nicht gefunden
Table "ACCOUNT" not found; SQL statement:
insert into foo.Account (uid, password_hash, login) values (null, ?, ?) [42102-197]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:357)
At this point I am trying to create a UserAccount in my testcase
This is the defined UerEntity
#AllArgsConstructor
#NoArgsConstructor
#Data
#Entity
#Table(name = "foo.Account")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "uid")
private Long id;
#Column(name = "login")
private String username;
#Column(name = "password_hash")
private String password;
....
Here is the Testcase. The error occurs when the before mehod is called during the createUser Method.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
#AutoConfigureMockMvc
public class UserControllerTest {
private static final Logger LOG = LoggerFactory.getLogger(UserControllerTest.class);
#LocalServerPort
private int port;
TestRestTemplate restTemplate = new TestRestTemplate();
HttpHeaders headers = new HttpHeaders();
#Autowired
private WfcSpringRestApplication controller;
#Autowired
private UserRepository repository;
#Autowired
private MockMvc mvc;
private AuthenticationToken authToken;
#Before
public void before() throws Exception {
headers = new HttpHeaders();
UserEntity user = createTestUser(TEST_ADMIN_USER, TEST_ADMIN_MD5_PW, UserRight.ADMIN);
UserEntity userService = createTestUser(TEST_SERVICE_USER, TEST_ADMIN_MD5_PW, UserRight.SERVICE);
getAuthenticationTokenForTestUser(user);
}
private UserEntity createTestUser(String username, String md5_password, UserRight right) {
UserEntity ue = new UserEntity();
ue.setUsername(username);
ue.setPassword(md5_password);
UserRole roleAdmin = new UserRole();
roleAdmin.setRight(right);
ue.getRoles().put(roleAdmin.getRight(), roleAdmin);
repository.save(ue);
return ue;
}
#Test
public void contextLoads() {
assertThat(controller).isNotNull();
}
In the error message there is the correct table name "could not prepare statement [insert into foo.Account" why it throws the exception that the table account is not found?
I faced similar error and I tried pretty much every solution mentioned on other websites such as DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1; DB_CLOSE_ON_EXIT=FALSE; IGNORECASE=TRUE
But nothing worked for me.
For Spring Boot 2.4+ use spring.jpa.defer-datasource-initialization=true in application.properties (mentioned here - https://stackoverflow.com/a/68086707/8219358)
Another way that worked for me was renaming data.sql to import.sql
I found it here - https://stackoverflow.com/a/53179547/8219358
I realize other solutions are more logical but none of them worked for me and this did.
I'm trying to get a simple mapping done but I am having issues.
Basically what I have in Mysql is a User table with just one column that is a varchar(255) named Username that is the primary key.
I have one other table called notes which has a primary auto-generating key that is an int, a date column, varchar name and contents columns and a varchar(255) called owner which should contain a user's username.
This is tagged as a foreign key referencing Users (Username).
The code to get the session factory is this:
private static SessionFactory createSessionFactory() {
SessionFactory sessionFactory;
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
configuration.addAnnotatedClass(Note.class);
configuration.addAnnotatedClass(User.class);
StandardServiceRegistry serviceRegistry = new
StandardServiceRegistryBuilder().applySettings(
configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
This works fine without the line that adds the annotated class 'Note' so it is probably an issue with that class. The error is this:
Could not get constructor for
org.hibernate.persister.entity.SingleTableEntityPersister
org.hibernate.MappingException
Full stacktrace:
Full classes are available here:
Note
User
Test
UPDATE: fixed Owner/OwnerName variable misnaming however I now get this error: ERROR:
Cannot add or update a child row: a foreign key constraint fails
(notes.notes, CONSTRAINT notes_ibfk_1 FOREIGN KEY (Owner)
REFERENCES users (username))
Stacktrace.
The issue is in the Note class.
For the variable owner, setter method name is not proper.
Instead of
public void setOwnerName(String u) {
this.owner = u;
}
It should be
public void setOwner(String u) {
this.owner = u;
}
This should resolve the issue.
you should first save user then save note in your test class.
your code should be like this.
#Test
public void testSave() {
Session session = factory.openSession();
Date date = Date.valueOf(LocalDate.now());
User user = new User("Joseph");
Note note = new Note();
note.setName("Joseph's note");
note.setContents("blah blah blah");
note.setOwnerName("Joseph");
session.beginTransaction();
session.save(user);
session.save(note);
session.getTransaction().commit();
session.close();
System.out.println(date);
}
but with this code, you just have foreign key in database and you don't have relation in your code.
your note enttiy can be like below code.
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "username")
public String getOwner() {
return owner;
}
finally your test class can be like :
#Test
public void testSave() {
Session session = factory.openSession();
Date date = Date.valueOf(LocalDate.now());
User user = new User("Joseph");
Note note = new Note();
note.setName("Joseph's note");
note.setContents("blah blah blah");
note.setOwner(user);
session.beginTransaction();
session.save(user);
session.save(note);
session.getTransaction().commit();
session.close();
System.out.println(date);
}
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.
Hibernate hbm2ddl only creates 2 of 6 tables when the SessionFactory is creating.
Example of table that is created:
#Entity
#Table(name = "AUTHORITIES")
public class Authorities {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "AUTHORITY", nullable=false)
private String authority;
#ManyToOne
#JoinColumn(name = "USERNAME", nullable=false)
private User user;
...getters, setters...
Example of table that isn't created:
#Entity
#Table(name = "EXERCISES")
public class Exercise {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "NAME", nullable=false)
private String name;
#Column(name = "DESCRIPTION")
private String description;
...getters, setters...
Hibernate properties
hibernate.show_sql=true
hibernate.hbm2ddl.auto=create
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
SQL from console:
Hibernate: drop table if exists AUTHORITIES cascade
Hibernate: drop table if exists USERS cascade
Hibernate: create table AUTHORITIES (id serial not null, AUTHORITY varchar(255) not null, USERNAME varchar(255) not null, primary key (id))
Hibernate: create table USERS (USERNAME varchar(255) not null, ENABLED boolean not null, PASSWORD varchar(255) not null, primary key (USERNAME))
Hibernate: alter table AUTHORITIES add constraint FKl8gm1dxqhdixkdsm1p7er9c21 foreign key (USERNAME) references USERS
Database is remote ElephantSQL.
Alright, as duffymo pointed, the configuration was wrong.
#Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
Properties props = new Properties();
// Setting JDBC properties
props.put(DRIVER, env.getProperty("driver"));
props.put(URL, env.getProperty("jdbcUrl"));
props.put(USER, env.getProperty("username"));
props.put(PASS, env.getProperty("password"));
// Setting Hibernate properties
props.put(SHOW_SQL, env.getProperty("hibernate.show_sql"));
props.put(HBM2DDL_AUTO, env.getProperty("hibernate.hbm2ddl.auto"));
props.put(DIALECT, env.getProperty("hibernate.dialect"));
// Setting C3P0 properties
props.put(C3P0_MIN_SIZE, env.getProperty("hibernate.c3p0.min_size"));
props.put(C3P0_MAX_SIZE, env.getProperty("hibernate.c3p0.max_size"));
props.put(C3P0_ACQUIRE_INCREMENT, env.getProperty("hibernate.c3p0.acquire_increment"));
props.put(C3P0_TIMEOUT, env.getProperty("hibernate.c3p0.timeout"));
props.put(C3P0_MAX_STATEMENTS, env.getProperty("hibernate.c3p0.max_statements"));
factoryBean.setHibernateProperties(props);
factoryBean.setAnnotatedClasses(User.class, Authorities.class, Exercise.class, ExerciseUnit.class, Routine.class, Training.class);
return factoryBean;
I've forget to set new entities in:
factoryBean.setAnnotatedClasses(User.class, Authorities.class, Exercise.class, ExerciseUnit.class, Routine.class, Training.class);
I am trying to test one of my services and it suddenly fails with the following exception.
I am trying to figure out what is causing this exception to be thrown:
Caused by: org.h2.jdbc.JdbcSQLException: Table "XYZ" not found; SQL statement:
insert into xyz (id, xx_id, yy_id, order, path, place_id, primary) values (null, ?, ?, ?, ?, ?, ?) [42102-183]
Connection:
Creating new JDBC Driver Connection to [jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false]
PersistenceContext used for testing:
#Configuration
public class PersistenceContext {
#Bean
public EmbeddedDatabase dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:sql/db-schema.sql")
.build();
}
}
db-schema.sql
CREATE TABLE xyz(
id int(11) NOT NULL,
xx_id int(11) NULL,
yy_id int(11) NULL,
path varchar(200) NOT NULL,
date_time_added datetime NOT NULL,
"order" int(11) DEFAULT NULL,
"primary" bit(1) DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_xx FOREIGN KEY (xx_id) REFERENCES xx (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_yy FOREIGN KEY (yy_id) REFERENCES yy (id) ON DELETE NO ACTION ON UPDATE NO ACTION
);
The class where the error is thrown
private Image addXyzForXX(MultipartFile file, Xx xx) throws ... {
String destDir = resourceService.getPlacesUploadDir();
Xyz xyz = new Xyz();
xyz.setXx(xx);
String filePath = imageUploadService.upload(file, destDir);
java.util.Date dt = new java.util.Date();
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime = sdf.format(dt);
xyz.setPath(filePath);
xyz.setDateTimeAdded(currentTime);
xyz.setOrder(1);
xyz.setPrimary(true);
xyz = xyzRepository.save(xyz);
return xyz;
}
Xyz Repository
#Repository
public interface XyzRepository extends PagingAndSortingRepository<Xyz, Long> {
}
#Entity
#Table(name = "xyz")
public class Xyz{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String path;
private String dateTimeAdded;
private Integer order;
private Boolean primary;
#ManyToOne(optional=true)
#JoinColumn(name = "xxId")
private Xx xx;
[getters and setters for each field]
}
The testing class
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
public class ImageServiceTest {
#Autowired
private XyzService xyzService;
#Autowired
private XxService xxService;
#Test
public void testArgumentsNullity3() throws Exception {
XX xx = new XX("a", "b", "c", "d", "e");
xx= xxService.addXx(xx);
xyzService.addImage(XxService.Scope.XX, xx,new MockMultipartFile("a","a","image/jpeg", new byte[1024]));
}
}
I found the actual problem.
The exception message I showed in my initial post is missing something important. In the stacktrace there was something else that says "bad sql grammar". I was focused on "images" table because this was the first thing in the stack trace.
After I checked all the possible known issues, I tried to rename the fields from my entity because I thought it will H2 will interpret them as keywords.
After renaming the fields, everything worked just fine.