I am getting LazyInitializationException while using createQuery(), load() or get() methods. Configuration is based on annotations.
This is exception which I am getting:
Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate lt.package.to.Setting_$$_jvstfff_0.toString()
Schema structure:
CREATE TABLE IF NOT EXISTS `Settings` (
`key` varchar(255) COLLATE utf8_lithuanian_ci NOT NULL,
`value` varchar(255) COLLATE utf8_lithuanian_ci DEFAULT NULL,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_lithuanian_ci;
PersistenceConfig.java
#Configuration
#ComponentScan(basePackages= {"lt.setkus.sandbox.persistence"})
#EnableTransactionManagement
#PropertySource({"/WEB-INF/properties/configuration.properties"})
public class PersistenceConfig {
#Autowired
private Environment environment;
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", environment.getProperty("hibernate.hbm2ddl.auto"));
properties.setProperty("hibernate.dialect", environment.getProperty("hibernate.dialect"));
properties.setProperty("hibernate.globally_quoted_identifiers", "true");
return properties;
}
#Bean
public LocalSessionFactoryBean provideSessionFactoryBean() {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(getDataSource());
localSessionFactoryBean.setPackagesToScan("lt.setkus.sandbox.persistence.domain");
localSessionFactoryBean.setHibernateProperties(getHibernateProperties());
return localSessionFactoryBean;
}
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getProperty("jdbc.driver"));
dataSource.setUrl(environment.getProperty("jdbc.url"));
dataSource.setUsername(environment.getProperty("jdbc.user"));
dataSource.setPassword(environment.getProperty("jdbc.password"));
return dataSource;
}
#Bean
#Autowired
public HibernateTransactionManager provideTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager hibernateTransactioManager = new HibernateTransactionManager();
hibernateTransactioManager.setSessionFactory(sessionFactory);
return hibernateTransactioManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor provideExceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public PostItemRepository postItemRepository() {
return new FacebookPostRepository();
}
#Bean
public PostPersistenceService postPersistenceService(PostItemRepository postItemRepository) {
return new PostPersistenceEventHandler(postItemRepository);
}
#Bean
public SettingRepository provideSettingRepository() {
return new SettingDatabaseRepository();
}
#Bean
public SettingPersistenceService provideSettingPersistenceService(SettingRepository settingRepository) {
return new SettingPersistenceEventHandler(settingRepository);
}
}
Domain model class
#Entity
#Table(name = "settings")
public class Setting implements Serializable {
#Id
#Column(name = "key", unique = true)
private String key;
#Column(name = "value")
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public SettingDetails toSettingDetails() {
SettingDetails settingDetails = new SettingDetails();
BeanUtils.copyProperties(this, settingDetails);
return settingDetails;
}
public static Setting fromSettingDetails(SettingDetails settingDetails) {
Setting setting = new Setting();
BeanUtils.copyProperties(settingDetails, setting);
return setting;
}
}
Service layer
public class SettingDatabaseRepository implements SettingRepository {
#Autowired
private SessionFactory sessionFactory;
#Override
#Transactional
public Setting get(final String key) {
return (Setting)sessionFactory.getCurrentSession().get(Setting.class, key);
}
}
I really can't find any mistake which is causing these exceptions. What I am doing wrong?
The proper way to get object by its id will be
public Setting get(final String key) {
return (Setting) sessionFactory.getCurrentSession().get(Setting.class, key);
}
Related
I have two entities, two controllers for that entities, clean java config for spring configuration. One controller for entity works fine, I can add data to database. Second controller, which is same just another entity wont work, I cant add data to database.
This is EmployeeDetailController class:
#Controller
#RequestMapping("/employeeDetail")
public class EmployeeDetailController {
#Autowired
private EmployeeService employeeService;
#GetMapping("/listEmployeeDetail")
public String listEmployeeDetail(Model theModel) {
// get customers from the service
List<EmployeeDetail> theEmployeeDetail = employeeService.listEmployeeDetail();
// add the customers to the model
theModel.addAttribute("employeeDetails", theEmployeeDetail);
return "list-employeeDetail";
}
#GetMapping("/showFormForAddEmployeeDetail")
public String showFormForAddEmployeeDetail(Model theModel) {
// create model attribute to bind form data
EmployeeDetail theEmployeeDetail = new EmployeeDetail();
theModel.addAttribute("employeeDetail", theEmployeeDetail);
return "addNewEmployeeDetailForm";
}
#PostMapping("/addNewEmployeeDetail")
public String addNewEmployeeDetail(#ModelAttribute("employeeDetail") EmployeeDetail theEmployeeDetail) {
// save the customer using our service
employeeService.addNewEmployeeDetail(theEmployeeDetail);
return "redirect:/employeeDetail/listEmployeeDetail";
}
}
And this is addNewEmployeeDetailForm.jsp part of code for that:
<form:form action="addNewEmployeeDetail" modelAttribute="employeeDetail" method="POST">
When I run it, app open me a form to add data, after I click save, its 404 not found.
While this is my another controller:
EmployeeController:
#Controller
#RequestMapping("/employee")
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#GetMapping("/list")
public String listEmployee(Model theModel) {
// get customers from the service
List<Employee> theEmployee = employeeService.listEmployee();
// add the customers to the model
theModel.addAttribute("employees", theEmployee);
return "list-employee";
}
#GetMapping("/showFormForAdd")
public String showFormForAdd(Model theModel) {
// create model attribute to bind form data
Employee theEmployee = new Employee();
theModel.addAttribute("employee", theEmployee);
return "addNewEmployeeForm";
}
#PostMapping("/addNewEmployee")
public String addNewEmployee(#ModelAttribute("employee") Employee theEmployee) {
// save the customer using our service
employeeService.addNewEmployee(theEmployee);
return "redirect:/employee/list";
}
}
And addNewEmployeeForm.jsp part of code for form to add data:
<form:form action="addNewEmployee" modelAttribute="employee" method="POST">
That works fine, while first controller doesnt work.
This is my DemoAppConfig:
#Configuration
#EnableWebMvc
#ComponentScan("ets")
#EnableTransactionManagement
#PropertySource("classpath:persistence-mysql.properties")
public class DemoAppConfig implements WebMvcConfigurer {
#Autowired
private Environment env;
private Logger logger = Logger.getLogger(getClass().getName());
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public DataSource dataSource() {
// create connection pool
ComboPooledDataSource myDataSource = new ComboPooledDataSource();
// set the jdbc driver
try {
myDataSource.setDriverClass("com.mysql.jdbc.Driver");
}
catch (PropertyVetoException exc) {
throw new RuntimeException(exc);
}
// for sanity's sake, let's log url and user ... just to make sure we are reading the data
logger.info("jdbc.url=" + env.getProperty("jdbc.url"));
logger.info("jdbc.user=" + env.getProperty("jdbc.user"));
// set database connection props
myDataSource.setJdbcUrl(env.getProperty("jdbc.url"));
myDataSource.setUser(env.getProperty("jdbc.user"));
myDataSource.setPassword(env.getProperty("jdbc.password"));
// set connection pool props
myDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize"));
myDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize"));
myDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize"));
myDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime"));
return myDataSource;
}
// need a helper method
// read environment property and convert to int
private int getIntProperty(String propName) {
String propVal = env.getProperty(propName);
// now convert to int
int intPropVal = Integer.parseInt(propVal);
return intPropVal;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
new String[]{"ets.entity"});
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager
= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "update");
hibernateProperties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return hibernateProperties;
}
}
EmployeeDAOImpl:
#Override
public void addNewEmployeeDetail(EmployeeDetail theEmployeeDetail) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(theEmployeeDetail);
}
EmployeeServiceImpl:
#Override
#Transactional
public void addNewEmployeeDetail(EmployeeDetail theEmployeeDetail) {
employeeDAO.addNewEmployeeDetail(theEmployeeDetail);
}
I noticed that in my entity class EmployeeDetails constructor:
public EmployeeDetail(int workExperience, String hobby, String nationality) {
this.workExperience = workExperience;
this.hobby = hobby;
this.nationality = nationality;
}
is never used. While constructor from entity Employee is used.
SQL
use `etsystem`;
create table employee (
employee_id BIGINT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR(30) NOT NULL,
email VARCHAR(30) NOT NULL,
PRIMARY KEY (employee_id)
);
create table employee_detail (
employee_detail_id BIGINT NOT NULL,
nationality VARCHAR(30) NOT NULL,
hobby VARCHAR(30) NOT NULL,
work_experience int(11) NOT NULL,
PRIMARY KEY (employee_detail_id),
CONSTRAINT employee_details FOREIGN KEY (employee_detail_id) REFERENCES employee (employee_id) ON DELETE NO ACTION ON UPDATE NO ACTION
);
First time I encounter this problem. The situation is:
I have more than 100 SQL databases, each one correspond to a different company and each one have the same three tables (same table names, same column names, same column data type).
Is there some way to map all these databases dynamically?
With dynamically I mean to have one class to which I can refer and make any CRUD operation.
After some research I could see what I wanted to do:
Basically I needed to change my data source in run time, for that I used a spring framework interface called AbstracRoutingDataSource.
Example:
Implementing AbstractRoutingDataSource:
public class MultiRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getCurrentDb();
}
}
DatabaseContextHolder:
public class DBContextHolder {
private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
public static void setCurrentDb(DBTypeEnum dbType) {
contextHolder.set(dbType);
}
public static DBTypeEnum getCurrentDb() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
Database type enum:
public enum DBTypeEnum{
DATASOURCE1("DATASOURCE1"),
DATASOURCE2("DATASOURCE2");
DBTypeEnum(final String dbTypeEnum){
this.dbTypeEnum = dbTypeEnum;
}
private String dbTypeEnum;
public String dbTypeEnum(){
return dbTypeEnum;
}
}
Persistence configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "base.packages.path",
entityManagerFactoryRef = "multiEntityManager",
transactionManagerRef = "multiTransactionManager"
)
public class PersistenceConfiguration {
private final String PACKAGE_SCAN = "base.package.path";
#Bean(name = "dataSource1")
#ConfigurationProperties("spring.datasource1")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dataSource2")
#ConfigurationProperties("spring.datasource2")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
#Bean(name = "multiRoutingDataSource")
public DataSource multiRoutingDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.DATASOURCE1, dataSource1());
targetDataSources.put(DBTypeEnum.DATASOURCE2, dataSource2());
MultiRoutingDataSource multiRoutingDataSource = new MultiRoutingDataSource();
multiRoutingDataSource.setDefaultTargetDataSource(dataSource1());
multiRoutingDataSource.setTargetDataSources(targetDataSources);
return multiRoutingDataSource;
}
#Bean(name = "multiEntityManager")
public LocalContainerEntityManagerFactoryBean multiEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(multiRoutingDataSource());
em.setPackagesToScan(PACKAGE_SCAN);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
return em;
}
#Bean(name = "multiTransactionManager")
public PlatformTransactionManager multiTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
multiEntityManager().getObject());
return transactionManager;
}
#Bean(name = "dbSessionFactory")
public LocalSessionFactoryBean dbSessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(multiRoutingDataSource());
sessionFactoryBean.setPackagesToScan(PACKAGE_SCAN);
sessionFactoryBean.setHibernateProperties(hibernateProperties());
return sessionFactoryBean;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", true);
properties.put("hibernate.format_sql", true);
return properties;
}
}
Then you need to have all of your database information in a .properties file:
spring.datasource1.jdbcUrl=jdbc:sql:sql-url:3306/datasource1
spring.datasource1.username=username
spring.datasource1.password=password
spring.datasource1.driver-class-name= Driver
spring.datasource2.jdbcUrl=jdbc:sql:sql-url:3306/datasource2
spring.datasource2.username=username
spring.datasource2.password=password
spring.datasource2.driver-class-name= Driver
Then you need to map your entity:
#Entity
#Table(name = "table_name")
#Getter
#Setter
public class MyEntity implements Serializable {
#Id
#Column(name = "ID", columnDefinition = "varchar(17)")
private String id;
//more fields...
}
I used spring CrudRepositories interface for this entity
public interface IMyEntityRepository extends CrudRepository<MyEntity, String> {
}
Finally my controller is prepared to change datasource depending on a JSON field in my request.
JSON:
{
"dataSource":"DATASOURCE1"
//more fields ...
}
RESTController:
#PutMapping("/url/{id}")
public ResponseEntity<?> editMyEntity(#RequestBody RequestObject request #PathVariable String id){
DBContextHolder.setCurrentDb(DBTypeEnum.valueOf(request.getDataSource);
iMyEntitiRepository.getMyEntity(id);
//...
}
Currently I have to connect two Oracle Datasources in my Spring Boot proyect. I think that I did well the configuration because Spring validated my connection but when I call a service this always response me "Empty". I think is because the TransactionManager don't change.
This is my project structure:
- project
⊢---- config
⊢---- controllers
∟---- models
⊢---- dao
| ⊢---- db1
| ∟---- db2
⊢---- entity
| ⊢---- db1
| ∟---- db2
⊢---- services
⊢---- db1
∟---- db2
This are my entities classes:
package project.models.entity.db1;
//// IMPORTS
#Entity(name = "User")
#Table(name = "T_USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String name;
private String surname;
private String username;
private String password;
////// CONSTRUCT
////// GETTERS & SETTERS
////// HASCODE, EQUALS AND TOSTRING
}
package project.models.entity.db2;
//// IMPORTS
#Entity(name = "Shareholder")
#Table(name = "Shareholder")
public class Shareholder implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
private Integer numberTitles;
////// CONSTRUCT
////// GETTERS & SETTERS
////// HASCODE, EQUALS AND TOSTRING
}
This are my repositories classes:
package project.models.dao.db1;
//// IMPORTS
#PersistenceContext(name = "db1")
#Repository
public interface IUserDao extends JpaRepository<User, Long> {
}
package project.models.dao.db2;
//// IMPORTS
#PersistenceContext(name = "db2")
#Repository
public interface IShareholderDao extends JpaRepository<Shareholder, Integer> {
}
This are my services classes:
package project.models.services.db1.impl;
//// IMPORTS
#Service
public class UserServiceImpl implements IUserService {
#Autowired
private IUserDao userDao;
#Override
#Transactional("transactionManager")
public List<User> findAll() {
return userDao.findAll();
}
#Override
#Transactional("transactionManager")
public User findById(Long id) {
return userDao.findById(id).orElse(null);
}
package project.models.services.db2.impl;
//// IMPORTS
#Service
public class ShareholderServiceImpl implements IShareholderService {
#Autowired
private IShareholderDao shareholderDao;
#Override
#Transactional("db2TransactionManager")
public List<Shareholder> findAll() {
return shareholderDao.findAll();
}
#Override
#Transactional("db2TransactionManager")
public Shareholder findById(Integer id) {
return shareholderDao.findById(id).orElse(null);
}
}
And this are my config classes:
package project.config;
//// IMPORTS
#Configuration
#ConfigurationProperties(prefix = "oracle1")
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager", basePackages = {
"project.models.dao.db1" })
#EnableTransactionManagement
#Validated
public class Db1Config {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "project.models.entity.db1" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
em.setPersistenceUnitName("db1");
return em;
}
#Bean(name = "dataSource")
DataSource dataSource() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
return dataSource;
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() throws SQLException {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.hbm2ddl.auto", "validate");
return properties;
}
}
package project.config;
//// IMPORTS
#Configuration
#ConfigurationProperties(prefix = "oracle2")
#EnableJpaRepositories(entityManagerFactoryRef = "db2EntityManagerFactory", transactionManagerRef = "db2TransactionManager", basePackages = {
"project.models.dao.db2" })
#EnableTransactionManagement
#Validated
public class Db2Config {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
#Bean(name = "db2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean db2EntityManagerFactory() throws SQLException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(db2DataSource());
em.setPackagesToScan(new String[] { "project.models.entity.db2" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
em.setPersistenceUnitName("db2");
return em;
}
#Bean(name = "db2DataSource")
DataSource db2DataSource() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
return dataSource;
}
#Bean(name = "db2TransactionManager")
public PlatformTransactionManager db2TransactionManager() throws SQLException {
return new JpaTransactionManager(db2EntityManagerFactory().getObject());
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.hbm2ddl.auto", "validate");
return properties;
}
}
And my main application:
package project;
//// IMPORTS
#SpringBootApplication
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
#EnableCaching
public class ProjectApplication {
#Autowired
private IUserService userService;
#Autowired
private IShareholderService shareholderService;
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
#PostConstruct
void init() {
List<User> user = userService.findAll();
List<Shareholder> shareholder = shareholderService.findAll();
System.out.println();
System.out.println(user);
System.out.println(shareholder);
System.out.println();
}
}
And always response me:
[User [id=39, name=Jack, surname=Sparrow, ...], [...], ...]
[]
What is the error? Because I don't see it.
Thank you very much
In Db2Config class, package being scanned is different than Db1Config.
Db1Config:
em.setPackagesToScan(new String[] { "project.models.entity.db1" });
Db2Config
em.setPackagesToScan(new String[] { "project.models.dao.db2" });
My model is
import javax.annotation.Nonnull;
import javax.persistence.Entity;
import org.joda.time.DateTime;
#Entity
public class Network extends AbstractEntity {
private long networkId;
private String name;
private boolean active;
private DateTime createdAt;
private String createdBy;
private DateTime updatedAt;
private String updatedBy;
...
}
AbstractEntity is
#MappedSuperclass
public class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId() {
return id;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final AbstractEntity abstractEntity = (AbstractEntity) obj;
return id.equals(abstractEntity.id);
}
#Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
}
I have Repository where I want to add entity. I do
#Repository
public class NetworkRepositoryImpl implements NetworkRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
#Nonnull
public List<Network> findAll() {
//noinspection JpaQlInspection
final Query query = entityManager.createQuery("SELECT e FROM Network e");
return (List<Network>)query.getResultList();
}
#Nonnull
#Override
#Transactional
public Network save(#Nonnull final Network network) {
if (network.getId() == null) {
entityManager.persist(network);
return network;
} else {
return entityManager.merge(network);
}
}
}
and I Test it as
#Test
public void testAllNetworks() {
final Network network = new Network(1, "US", true, DateTime.now());
final Network networkInDb = networkRepository.save(network);
assertNotNull(networkInDb.getId());
final List<Network> networks = networkRepository.findAll();
assertTrue(networks.isEmpty());
}
my ApplicationConfig looks like
#Configuration
#ComponentScan
#EnableJpaRepositories
public class ApplicationConfig {
#Bean
public DataSource dataSource() {
final EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder();
embeddedDatabaseBuilder.setType(EmbeddedDatabaseType.H2);
return embeddedDatabaseBuilder.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.H2);
jpaVendorAdapter.setGenerateDdl(true);
final LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
localContainerEntityManagerFactoryBean.setPackagesToScan("com.org.comma.persistence");
localContainerEntityManagerFactoryBean.setDataSource(dataSource());
localContainerEntityManagerFactoryBean.setPersistenceUnitName("test-comma-pu");
return localContainerEntityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
My test fails on line
assertNotNull(networkInDb.getId());
meaning the entity is not persisted
What I am doing wrong here?
In my case, I was using wrong annotation.
I was using
import javax.transaction.Transactional;
instead, what I needed was
import org.springframework.transaction.annotation.Transactional;
Atleast I see it try persisting and failing somewhere else
If your test fails because Id value is null, then it is expected behavior.
When hibernate persist an entity, it will not fetch the auto-generated key value automatically. You should either find the entity again or refresh it manually.
In similar situation, I usually refresh the entity like below.
entityManager.persist(network);
entityManager.flush();
entityManager.refresh(network);
P.S: You should flush before refreshing to make it reliable.
I am using SchemaExporter to generate ddl script.
public static void main(String[] args) throws Exception {
SchemaDDLGenerator.execute(Dialect.ORACLE, Key.class);
}
Key class:
#Entity
public class Key extends AbstractEntity {
private static final long serialVersionUID = 7467094398251027638L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "KEY_SID")
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private API granter;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private API grantee;
API entity class is an entity class with simple columns.
Schema Generator:
public class SchemaDDLGenerator {
public static void execute(Dialect dialect, Class<?>... classes) {
Configuration configuration = new Configuration();
configuration.setProperty(Environment.DIALECT, dialect.getClassName());
for (Class<?> entityClass : classes) {
configuration.addAnnotatedClass(entityClass);
}
SchemaExport schemaExport = new SchemaExport(configuration);
schemaExport.setDelimiter(";");
boolean consolePrint = true;
boolean exportInDatabase = false;
schemaExport.setOutputFile(String.format("ddl_%s_drop.%s ", new Object[] { dialect.name().toLowerCase(), "sql" }));
schemaExport.drop(false, false);
schemaExport.setOutputFile(String.format("ddl_%s.%s ", new Object[] { dialect.name().toLowerCase(), "sql" }));
schemaExport.create(consolePrint, exportInDatabase);
}
}
It is working fine for entities that does not have any relations in it but it is throwing error for the ones having #OneToMany or #ManyToOne relations.
Exception in thread "main" org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.example.data.model.ClientRelationship.grantee references an unknown entity: com.example.data.model.Client
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:109)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1532)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1453)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1358)
at org.hibernate.cfg.Configuration.generateDropSchemaScript(Configuration.java:934)
at org.hibernate.tool.hbm2ddl.SchemaExport.<init>(SchemaExport.java:188)
at org.hibernate.tool.hbm2ddl.SchemaExport.<init>(SchemaExport.java:156)
at com.example.util.db.SchemaDDLGenerator.execute(SchemaDDLGenerator.java:16)
at com.example.data.generator.SchemaGenerator.main(SchemaGenerator.java:67)
Following is java based hibernate configuration
#Configuration
#Import({ DataConfigDefault.class, DataConfigCi.class })
#ComponentScan(basePackages = "com.example.data.repository.util")
#EnableJpaRepositories(basePackages = { "com.example.data.repository" })
#EnableTransactionManagement(mode = AdviceMode.PROXY)
public class DataConfig {
private static Logger logger = LoggerFactory.getLogger(DataConfig.class);
#Autowired
private Environment environment;
#PostConstruct
public void postConstruct() {
logger.debug("active profiles: ({})", StringUtils.join(environment.getActiveProfiles(), ","));
}
static DataSource createH2Datasource(String jdbcUrl) {
logger.debug("creating h2 data source using: {}", jdbcUrl);
JdbcDataSource ds = new JdbcDataSource();
ds.setURL(jdbcUrl);
ds.setUser("sa");
ds.setPassword("");
return ds;
}
#Value("classpath:seed-data.sql")
private Resource h2Schema;
#Value("classpath:test-data.sql")
private Resource h2DataScript;
#Autowired
#Bean
public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) {
final DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource);
initializer.setDatabasePopulator(databasePopulator());
return initializer;
}
private DatabasePopulator databasePopulator() {
final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(h2Schema);
populator.addScript(h2DataScript);
return populator;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(Environment env, DataSource dataSource) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(Boolean.TRUE);
vendorAdapter.setShowSql(Boolean.TRUE);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("merchant");
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.example.data.model");
factory.setDataSource(dataSource);
factory.setJpaProperties(jpaProperties());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
Properties jpaProperties() {
Properties props = new Properties();
props.put("hibernate.query.substitutions", "true 'Y', false 'N'");
props.put("hibernate.hbm2ddl.auto", "update");
props.put("hibernate.show_sql", "false");
props.put("hibernate.format_sql", "true");
return props;
}
}
Any idea?
The problem is that you're not adding all needed classes to schema generator. You can enable them in such way:
for (Class<Object> clazz : getClasses(packageName)) {
cfg.addAnnotatedClass(clazz);
}
private List<Class> getClasses(String packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<>();
ClassLoader cld = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
URL resource = cld.getResource(path);
File directory = new File(resource.getFile());
if (!directory.exists()) {
throw new ClassNotFoundException(packageName + " is not a valid package");
}
processDirectory(packageName, classes, directory);
return classes;
}