First: I'm probably just making a stupid mistake.
I'm working on converting an old project of mine from Spring XML to Javaconfig. The database is an in-memory HSQLDB database. Unfortunately, it's giving me this error:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL via JDBC Statement
(stacktrace)
Caused by: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: PUBLIC.T_AUTHORITY
(stacktrace)
Caused by: org.hsqldb.HsqlException: user lacks privilege or object not found: PUBLIC.T_AUTHORITY
Below are my PersistenceConfig.java and my SQL script:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "org.jason.application.repository.jpa",
entityManagerFactoryRef = "entityManagerFactoryBean")
public class ApplicationPersistenceConfig {
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(emf);
return jpaTransactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName("default");
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactory.setJpaDialect(new HibernateJpaDialect());
entityManagerFactory.setPackagesToScan("org.jason.application.repository.model");
entityManagerFactory.setJpaPropertyMap(hibernateJpaProperties());
return entityManagerFactory;
}
#Bean
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:mem:testdb");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Map<String, ?> hibernateJpaProperties() {
HashMap<String, String> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.import_files", "insert-data.sql");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
properties.put("hibernate.show_sql", "false");
properties.put("hibernate.format_sql", "false");
properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
properties.put("hibernate.c3p0.min_size", "2");
properties.put("hibernate.c3p0.max_size", "5");
properties.put("hibernate.c3p0.timeout", "300"); // 5mins
return properties;
}
}
and
CREATE TABLE PUBLIC.T_USER (
USERID INTEGER NOT NULL PRIMARY KEY,
USERNAME VARCHAR_IGNORECASE(50) NOT NULL,
PASSWORD VARCHAR_IGNORECASE(50) NOT NULL,
ENABLED BOOLEAN NOT NULL,
CREATE UNIQUE INDEX IX_USERNAME ON T_USER(USERNAME);
CREATE TABLE PUBLIC.T_AUTHORITY (
AUTHORITYID INTEGER NOT NULL PRIMARY KEY,
USERID INTEGER NOT NULL,
-- USERNAME VARCHAR_IGNORECASE(50) NOT NULL,
AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,
CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERID) REFERENCES USERS(USERID));
CREATE UNIQUE INDEX IX_AUTH_USERNAME ON T_AUTHORITY (USERID,AUTHORITY);
INSERT INTO T_USER(USERNAME, PASSWORD, ENABLED) VALUES (1, 'jason','password', true);
INSERT INTO T_AUTHORITY(AUTHORITYID, USERID, AUTHORITY) VALUES (1, 1, "ROLE_ADMIN");
Can anyone see whatever stupid mistake I made?
Jason
It was a dumb mistake, just like I thought.
The following two hibernate properties are incompatible with one another:
properties.put("hibernate.hbm2ddl.import_files", "insert-data.sql");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
Both have the effect of creating the schema.
Related
I want to use two database schemas: one with static data (which cannot be changed), the other with dynamic data (which will change constantly during the execution of the Spring application).
In application.properties I have prescribed the following:
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/XXXXXX
spring.datasource.username=root
spring.datasource.password=root
spring.second-datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/YYYYYY
spring.second-datasource.username=root
spring.second-datasource.password=root
But what should I do now with the spring.jpa.hibernate.ddl-auto parameter?
For one scheme I want to install:
spring.jpa.hibernate.ddl-auto=create
And for the other (where the data is always static):
spring.jpa.hibernate.ddl-auto=update
or
spring.jpa.hibernate.ddl-auto=none
You have an issue that you need two different datasources. You should define them as 2 different beans and autowire them seperately. For clarity it's advisable to also use 2 different properties files.
You can create a Configuration class and use specific properties file to populate fields like so:
#Configuration
#PropertySource("classpath:db.properties")
public class Config {
#Value("${db.driverClassName}")
String driverClassName;
#Value("${db.url}")
String url;
#Value("${db.username}")
String username;
#Value("${db.password}")
String password;
#Bean("datasourceId")
public DataSource dataSource() {
var dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
{ "com.foo.bar" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "create-drop");
return hibernateProperties;
}
}
Here is the application.properties of my Spring Boot 2.5 :
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME}
spring.datasource.username=${MYSQL_USER:root}
spring.datasource.password=${MYSQL_PASSWORD:root}
I migrated my application from Spring MVC 4.3 and I don't use JPA on the latest version of Spring Boot.
So I configured a #Beandatasource like this:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getRequiredProperty("spring.datasource.url"));
dataSource.setUsername(env.getRequiredProperty("spring.datasource.username"));
dataSource.setPassword(env.getRequiredProperty("spring.datasource.password"));
return dataSource;
}
However, this does not work because instead of having the value of the environment variable, I have the name of the environment variable, i.e. ${MYSQL_USER:root} for example.
My question is what is Spring's recommended way here to set up environment variables much like Laravel's .env in my datasource() and also in the application.properties if I decide to use JPA later on?
The reason and the important point: I don't want to push my credentials on git
EDIT :
HibernateUtil :
static {
try {
Properties applicationProps = new Properties();
File file = ResourceUtils.getFile("classpath:application.properties");
InputStream input = new FileInputStream(file);
applicationProps.load(input);
Properties properties = new ApplicationConf().hibernateProperties();
// Configure datasource
properties.setProperty("hibernate.connection.driver_class", applicationProps.getProperty("spring.datasource.driver-class-name"));
properties.setProperty("hibernate.connection.url", applicationProps.getProperty("spring.datasource.url"));
properties.setProperty("hibernate.connection.username", applicationProps.getProperty("spring.datasource.username"));
properties.setProperty("hibernate.connection.password", applicationProps.getProperty("spring.datasource.password"));
properties.setProperty("hibernate.current_session_context_class", "thread");
properties.setProperty("hibernate.jdbc.batch_size", Integer.toString(BATCH_SIZE));
// Override some properties
properties.setProperty("hibernate.format_sql", "false");
properties.setProperty("hibernate.show_sql", "false");
} catch {}
...
}
Best regards,
You can use the #Value annotation to pull the properties and also their defaults.
#Value("${spring.datasource.username}")
private String userName;
#Bean
public DataSource dataSource() {
...
dataSource.setUsername(userName);
...
return dataSource;
}
You can also omit the default from the application.properties file and specify a default value for the property using Value annotation -
#Value("${spring.datasource.username:root}")
private String userName;
#Bean
public DataSource dataSource() {
...
dataSource.setUsername(userName);
...
return dataSource;
}
Or can even be injected in the method-
#Bean
public DataSource dataSource(#Value("${spring.datasource.username:root}") String userName) {
...
dataSource.setUsername(userName);
...
return dataSource;
}
Starting from a program that was connected to a mysql database, using spring, JPA and JDBC, I am trying to configure that application to use the H2 database in embedded mode.
With MYSQL everything works fine, but with H2 not.
I can not get H2 to return any records, although the records are there so if I do the same query through JDBC, if I see them.
The configuration that I have is the following:
#Configuration
#EnableJpaRepositories("yages.yagesserver")
#EnableTransactionManagement
public class JpaConfig {
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.H2)
.setName("yagesh2")
.ignoreFailedDrops(true)
.addScript("db/sql/create-db.sql")
.addScript("db/sql/insert-data.sql")
.generateUniqueName(false)
.build();
return db;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) throws NamingException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[]{"yages.yagesserver", "yages.yagesserver.dao"});
em.setPersistenceUnitName("yages-server");
em.setJpaVendorAdapter(jpaVendorAdapter());
em.afterPropertiesSet();
return em;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setDatabase(Database.H2);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
So if I write this code:
String s="SELECT cal_ano,cal_mes,cal_fecini,cal_fecfin from calendario where cal_ano=? and cal_mes = ?";
List<Calendario> cal =
jdbc.query(s, new Object[] { ano,mes},
(rs, rowNum) -> new Calendario(
rs.getInt("cal_ano"),rs.getInt("cal_mes"),rs.getDate("cal_fecini"),rs.getDate("cal_fecfin"))
);
System.out.println("getDatosSemana. Size "+cal.size()+ "Fecha Inicio: "+cal.get(0).getFechaInicio());
Optional<Calendario> calOpc = calendarioRepositorio.getCalendario(new CalendarioKey(ano - 1, mes));
System.out.println("getDatosSemana. Optional is present: "+calOpc.isPresent());
When I use JDBC I see that in the calendar table if the record exists, but when using JPA, it does not seem to find anything.
This is the output in my console:
getDatosSemana. Size 1Fecha Inicio: 2018-01-28
Hibernate: select calendario0_.cal_ano as cal_ano1_0_0_, calendario0_.cal_mes as cal_mes2_0_0_, calendario0_.cal_fecfin as cal_fecf3_0_0_, calendario0_.cal_fecini as cal_feci4_0_0_ from calendario calendario0_ where calendario0_.cal_ano=? and calendario0_.cal_mes=?
getDatosSemana. Optional is present: false
Of course I have the DAO classes and my repository that extends from a CrudRepository.
Any suggestions, please?
I'm sorry. I have found the error.
It was a failure foolishness. I called the JPA function with the parameters of year and month reversed.
Sometimes you do not see the obvious.
This is my hibernate configuration:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(final DataSource dataSource,
final Environment env)
{
final LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("...");
final Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
jpaProperties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.ejb.naming_strategy", env.getRequiredProperty("hibernate.ejb.naming_strategy"));
jpaProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
jpaProperties.put("hibernate.format_sql", env.getRequiredProperty("hibernate.format_sql"));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
Now I want to make a query in HQL (using #Query(value = "query")) with standard pagination settings like offset and limit. I know about query.setMaxResults() and query.setFirstResult(), but for that I need a Session (or do I?), but I didn't use sessions to configure Hibernate.
Can I use annotations only to specify the offset and limit to queries? Is there a way to use HQL to programmatically simulate query.setMaxResults() and query.setFirstResult()?
Following code may help you.
public Page<Product> findAll(Pageable pageable){
//custom page
PageRequest customPageable = new PageRequest(pageable.getPageNumber(), 100);
return productRepository.findAll(customPageable);
}
I want to configure Second-Level cache in hibernate. I find out official documentation of EhCache and did it. But i got exception:
Caused by: org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given; please either disable second level cache or set correct region factory using the hibernate.cache.region.factory_class setting and make sure the second level cache provider (hibernate-infinispan, e.g.) is available on the classpath.
at org.hibernate.cache.internal.NoCachingRegionFactory.buildEntityRegion(NoCachingRegionFactory.java:83) ~[NoCachingRegionFactory.class:4.3.3.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:364) ~[SessionFactoryImpl.class:4.3.3.Final]
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1857) ~[Configuration.class:4.3.3.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850) ~[EntityManagerFactoryBuilderImpl$4.class:4.3.4.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:843) ~[EntityManagerFactoryBuilderImpl$4.class:4.3.4.Final]
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:397) ~[ClassLoaderServiceImpl.class:4.3.3.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842) ~[EntityManagerFactoryBuilderImpl.class:4.3.4.Final]
at org.hibernate.jpa.HibernatePersistenceProvider.createContainerEntityManagerFactory(HibernatePersistenceProvider.java:150) ~[HibernatePersistenceProvider.class:4.3.4.Final]
It is not full exception message, but main info.
Below configuration of my web application:
private Properties hibernateProperties() {
return new Properties() {{
setProperty("hibernate.connection.characterEncoding", "UTF-8");
setProperty("hibernate.connection.charSet", "UTF-8");
setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect");
setProperty("hibernate.show_sql", "true");
setProperty("hibernate.format_sql", "true");
setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
setProperty("hibernate.enable_lazy_load_no_trans", "true");
setProperty("hibernate.cache.use_second_level_cache", "true");
setProperty("hibernate.cache.use_query_cache", "true");
setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");
/*setProperty("net.sf.ehcache.configurationResourceName", env.getProperty("net.sf.ehcache.configurationResourceName"));*/
}};
}
#Bean
LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan("com.smartestgift.*");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
And EntityManager configuration. I don't think thats the main problem here, but i will show it code:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource());
emf.setPackagesToScan("com.smartestgift.*");
//let Hibernate know which database we're using.
//note that this is vendor specific, not JPA
Map opts = emf.getJpaPropertyMap();
opts.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(va);
return emf;
}
I can't understand what's the problem. Why hibernate can't see that property?
UPDATED:
M. Denium - thank you! You were right, i delete entity manager and refactor sessionFactory method, now it look like this:
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan("com.smartestgift.*");
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.connection.characterEncoding", "UTF-8");
hibernateProperties.put("hibernate.connection.charSet", "UTF-8");
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect");
hibernateProperties.put("hibernate.show_sql", "true");
hibernateProperties.put("hibernate.format_sql", "true");
//hibernateProperties.put("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.put("hibernate.enable_lazy_load_no_trans", "true");
// second level cache
hibernateProperties.put("hibernate.cache.use_second_level_cache", "true");
hibernateProperties.put("hibernate.cache.use_query_cache", "true");
hibernateProperties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");
//hibernateProperties.put("net.sf.ehcache.configurationResourceName", env.getProperty("net.sf.ehcache.configurationResourceName"));
// testing
hibernateProperties.put("hibernate.bytecode.use_reflection_optimizer", false);
hibernateProperties.put("hibernate.check_nullability", false);
hibernateProperties.put("hibernate.search.autoregister_listeners", false);
sessionFactoryBean.setHibernateProperties(hibernateProperties);
try {
sessionFactoryBean.afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return sessionFactoryBean.getObject();
}
But now i got new problem, i dont know how implement OpenEntityManagerInViewInterceptor, because before deleting entity manager i have next code:
OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(entityManagerFactory().getObject());
But now i have not entityManagerFactory and i dont know how to make this interceptor now...