Insert Data only once after Hibernate started - java

I'm using hibernate 4.1.2
And I want insert some data into the table.
I know that it can be realized with sql in hibernate configuration in this way:
<property name="hibernate.hbm2ddl.import_files" value="/file1.sql,/file2.sql"/>.
But, are there any other ways insert automatically data only once in Java code after hibernate started?
I want do it like this:
public Role getRoleByName(EnumRole name)
{
return (Role) sessionFactory.getCurrentSession()
.createQuery("from Role where name = :name")
.setParameter("name", name).uniqueResult();
}
public void insertRoles(){
for(EnumRole role:EnumRole.values())
{
Role r=getRoleByName(role);
if(r==null)
{
r=new Role();
r.setName(role);
r.setDescription(role.getDescription());
sessionFactory.getCurrentSession().save(r);
}
}
EnumRole:
public enum EnumRole {
ROLE_CLIENT("РОЛЬ КЛИЕНТА"),
ROLE_ADMIN("РОЛЬ АДМИНСТРАТОРА"),
ROLE_CONSUMER("РОЛЬ КОМПАНЬОНА"),
ROLE_ANONYMOUS("НЕ АВТОРИЗОВАННЫЙ ПОЛЗОВАТЕЛЬ");
EnumRole(String descriptin)
{
this.descriptin=descriptin;
}
public String getDescription()
{
return this.descriptin;
}
private String descriptin;
}

You need to create any Spring bean with #PostConstruct annotated method and for example create transaction using PlatformTransactionManager:
#Service
public class AnyBean {
#Autowired
private PlatformTransactionManager transactionManager;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
private void init() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallback<Object>() {
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
// HERE YOU CODE
for(EnumRole role:EnumRole.values())
{
Role r = getRoleByName(role);
if(r==null)
{
r=new Role();
r.setName(role);
r.setDescription(role.getDescription());
sessionFactory.getCurrentSession().save(r);
}
}
return null;
}
});
}
}
I hope this helped.

Create a service with method insertRoles() marked with #Transactional and call service.insertRoles().
Or, more easy:
create a transaction
add roles
commit (or manage rollback if error occured)
Do this stuff after your application startup has been completed

Related

How to use Hibernate #Filter without #Transactional?

I am using Hibernate #Filter with Spring Data to add specific "where" clause for every query in my project. The problem is that it works as long as I use #Transactional annotation for my 'findAll' method. Is there any way to avoid using #Transactional? Why is it important here?
Here is the filter config:
#FilterDef(name = "testFilter",
parameters = #ParamDef(name = "company_id", type = "long"),
defaultCondition = "company_id=:companyId")
#Filter(name = "testFilter")
#EntityListeners(EmployeeListener.class)
#NoArgsConstructor
public class Employee {//basic fields}
Repository:
#Repository
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, UUID> {
}
Aspect that enables the filter:
#Aspect
#Component
public class TestAspect {
private final SecurityAspect securityAspect;
private final Logger logger;
#PersistenceContext
private EntityManager entityManager;
#Autowired
public TestAspect(SecurityAspect securityAspect, Logger logger) {
this.securityAspect = securityAspect;
this.logger = logger;
}
#Around("execution(* org.springframework.data.repository.CrudRepository+.findAll(..))")
public Object aroundFindAllTenantAware(ProceedingJoinPoint joinPoint) {
Session session = this.entityManager.unwrap(Session.class);
session.enableFilter("testFilter")
.setParameter("companyId", this.securityAspect.getCompanyId());
Object retValue = null;
try {
retValue = joinPoint.proceed();
} catch (Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
} finally {
session.disableFilter("testFilter");
}
return retValue;
}
}
And, finally, my service:
#Service
#Transactional
public class EmployeeApiServiceImpl {
private final EmployeeRepository repository;
#Autowired
public EmployeeApiServiceImpl(EmployeeRepository repository) {
this.employeeRepository = employeeRepository; }
#Override
public Response listProfiles(SecurityContext securityContext) {
List<Employee> employees = repository.findAll();
return Response.ok().entity(employees).build();
}
Without #Transactional annotation, it does not work. When I am debugging the aspect I can see that the filter was enabled but the query did not change. When I put the annotation, everything works fine. But I don't get why it is happening. This annotation is not supposed to be on read methods, plus in every tutorial everything works without it, but not in my case.
After some research I found a solution - using TransactionTemplate class. So I just need to put my code from aspect into method 'execute' and it will do the same as #Transactional annotation. But in this case, I don't have to annotate each method in each service as Transactional. These articles helped me a lot:
https://www.baeldung.com/spring-programmatic-transaction-management
https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
transactionTemplate.execute(status -> {
Session session = null;
Object result = null;
try {
session = entityManager.unwrap(Session.class);
session.enableFilter(Constants.FILTER_NAME)
.setParameter(Constants.PARAMETER_NAME, this.securityAspect.getCompanyId());
result = joinPoint.proceed();
} catch (Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
} finally {
if (session != null) {
session.disableFilter(Constants.FILTER_NAME);
}
}
return result;
});
The problem is the EntityManager proxy that is injected by Spring which only supports certain operations without a transaction. You'll need a custom proxy implementation if you want support for this. See org.springframework.orm.jpa.SharedEntityManagerCreator.SharedEntityManagerInvocationHandler#invoke

spring-hibernate transactional dont rollback

The HQL statement does not roll back when I use spring+hibernate, but session.saveOrUpdate () will;
UserService
#Service
#Transactional(rollbackFor=Exception.class)
public class UserService {
#Autowired
private BaseDao dao;
public int updateTest(){
int i = dao.updateUser();
int t = 1/0;
return i;
}
}
BaseDao
#Repository
public class BaseDao {
#Autowired
private SessionFactory sessionFactory;
private Session getSession(){
return sessionFactory.getCurrentSession();
}
public int updateUser(){
int i = 0;
/* String sql = "from Student where name = 'dengbojing'";
Query query = this.getSession().createQuery(sql);*/
Student s = new Student();
s.setId(1);
s.setAddress("1");
Query query = this.getSession().createQuery("update Student s set s.address = '1'");
query.executeUpdate();
//this.getSession().update(s);
return i;
}
}
Configuration class
#Configuration
#EnableConfigurationProperties(HibernateProperties.class)
#EnableTransactionManagement(proxyTargetClass=true)
public class HibernateConfig {
#Autowired
private HibernateProperties config;
#Bean(name="sessionFactory")
public LocalSessionFactoryBean localSessionFactoryBean(){
LocalSessionFactoryBean bean = new LocalSessionFactoryBean();
bean.setDataSource(dataSource());
bean.setHibernateProperties(config.getHibernateProperties());
bean.setPackagesToScan(config.getPackageToScan());
return bean;
}
#Bean
public DataSource dataSource(){
DruidDataSource source = new DruidDataSource();
source.setDriverClassName(config.getDatasource().getDriverClassName());
source.setUsername(config.getDatasource().getUsername());
source.setUrl(config.getDatasource().getUrl());
source.setPassword(config.getDatasource().getPassword());
return source;
}
#Bean
public HibernateTransactionManager txManager(){
HibernateTransactionManager manager = new HibernateTransactionManager();
manager.setSessionFactory(localSessionFactoryBean().getObject());
manager.setDataSource(dataSource());
return manager;
}
}
Spring transaction does not support the HQL statement, the problem plagued me for 2 days, I saw someone with similar problems, but did not solve the problem
I have made some tests with exact same versions and configuration.
I have to say that the update is never persisted in the database.
As a workaround, if you can implement your functionality in this way.. try to:
Find the desired Person entity
Update required fields
Do not invoke any other methods on the session object after that.. just leave it to the framework to update the changes on the transaction commit.
Example
Person person = session.get(Person.class, 1);
person.setAddress("1")
// other fields updated
return i;
Now, without the explicit bulk update using the executeUpate() there is no chance for an implicit commit by the provider.. This is theory so check it out.

Spring Boot and multiple databases

I've setup a basic spring project with a single database connection.
In the application.properties file I have the database settings:
spring.datasource.url = jdbc:mysql://192.168.1.19/ticket
spring.datasource.username = dbusername
spring.datasource.password = dbpassword
I've created a base DAO class which other DAOs extend:
#Transactional
public class Dao<E> {
#PersistenceContext
private EntityManager entityManager;
private Class<E> entityClass;
public Dao(Class<E> entityClass) {
this.entityClass = entityClass;
}
public void create(E object) {
entityManager.persist(object);
return;
}
public void delete(E object) {
if (entityManager.contains(object)) {
entityManager.remove(object);
} else {
entityManager.remove(entityManager.merge(object));
}
return;
}
#SuppressWarnings("unchecked")
public List<E> getAll() {
return entityManager.createQuery("from " + entityClass.getName()).getResultList();
}
public E get(long id) {
return entityManager.find(entityClass, id);
}
public void update(E object) {
entityManager.merge(object);
return;
}
}
Here's a sample entity that extends the base DAO:
#Repository
public class PersonDao extends Dao<Person> {
public PersonDao() {
super(Person.class);
}
}
Currently this uses a single database, but I need to be able to add a second database, and somehow define in each DAO which datasource to use. Each DAO will only use a single database, so there's no requirement for a DAO to be able to connect to multiple databases.
I've done some research, and that seems to suggest I need to use JdbcTemplate? but I can't seem to find a tutorial that matches my need. Also, at the minute the entityManager is injected into the DAO, but the JdbcTemplate examples I've looked at don't seem to use the entityManager, which is slightly confusing.
database.password1=<password1>
database.url1=jdbc\:mysql\://localhost\:3306/twodbone
database.username1=<username1>
database.password2=<password1>
database.url2=jdbc\:mysql\://localhost\:3306/twodbtwo
database.username2=<username2>
database.driverClassName=com.mysql.jdbc.Driver
In this way you can add the multiple databases and configure both hibernate.cfg.xml file and applicationContext.xml file also..
#Repository
public class FooRepository
{
#PersistenceContext
private EntityManager entityManager;
#Autowired(required = true)
private JdbcTemplate jdbcTemplate;
public void saveFoo(Foo foo)
{
this.entityManager.persist(foo);
}
public List<SomeReportPojo> getSomeReport()
{
return this.entityManager.queryForList("SELECT .. ",SomeProjectPojo.class);
}
}
this.jdbcTemplate should be kept rather than this.entityManager for jdbc templetes
this is simple example

Hibernate, Spring, querying in non-managed bean causes SessionException

I have a simple entity - Team, with name and rating properties (or maybe more).
Suppose I need to query my teams by multiple criteria.
So instead of adding multiple methods with signature like 'findByXYZAndZYX' to my service, I'd rather add following method :
Teams findTeams()
Implementation snippet:
#Autowired private SessionFactory sessionFactory;
...
#Override
public Teams getTeams() {
return new HibernateTeams(sessionFactory);
}
Now, Teams interface:
public interface Teams extends Iterable<Team> {
Teams withNameContaining(String name);
Teams withRatingGreaterThan(Integer rating);
}
and hibernate-specific implementation:
public class HibernateTeams implements Teams {
private static final String NAME_PROPERTY = "name";
private static final String RATING_PROPERTY = "rating";
private SessionFactory sessionFactory;
private Criteria criteria;
public HibernateTeams(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
criteria = getRootCriteria();
}
private Criteria getRootCriteria() {
return getCurrentSession().createCriteria(Team.class);
}
#Override
public HibernateTeams withNameContaining(String name) {
criteria.add(Restrictions.like(NAME_PROPERTY, name));
return this;
}
#Override
public Teams withRatingGreaterThan(Integer rating) {
criteria.add(Restrictions.gt(RATING_PROPERTY, rating));
return this;
}
#Override
public Iterator<Team> iterator() {
#SuppressWarnings("unchecked")
Collection<Team> result = criteria.list();
return result.iterator();
}
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
But now, using this in client code:
teamService
.getTeams()
.withNameContaining("someTeamName")
.withRatingGreaterThan(15)
I have an SessionException
org.hibernate.SessionException: Session is closed
I suppose this happens because of passing sessionFactory to non-managed class.
So there are a couple of questions here:
1) Is it possible to do this the way I wrote it? I tried to annotate my HibernateTeams with Transactional or sth, but it didn't help.
2) If I need to make my HibernateTeams spring-managed-bean and possibly inject SessionFactory into it, how can I do that? I've already tried with
#Component #Scope("prototype")
or #Configurable
but with no luck.
Thanks,

JPA - database gets updated only when method is #Transactional

I've encountered a problem I don't really understand - unless a method working with the DAO is annotated as #Transactional, the underlying database doesn't get updated. My app runs on JPA/Hibernate, Spring and Wicket. Why is that?
DAO:
#Repository(value = "userDao")
public class UserDaoJpa implements UserDao {
#PersistenceContext
private EntityManager em;
public User findById(Long id) {
return em.find(User.class, id);
}
public List findAll() {
Query query = em.createQuery("select e from User e");
return query.getResultList();
}
public User create(User user) {
em.persist(user);
return user;
}
public User update(User user) {
return em.merge(user);
}
public void delete(User user) {
user = em.merge(user);
em.remove(user);
}
}
Service:
#Service(value = "userManager")
public class UserManagerImpl implements UserManager {
#Autowired
UserDao dao;
public void setUserDao(UserDao dao) {
this.dao = dao;
}
public List getUsers() {
return dao.findAll();
}
public User getUser(String userId) {
return dao.findById(Long.valueOf(userId));
}
public void saveUser(User user) {
dao.update(user);
}
#Transactional
public void removeUser(User user) {
dao.delete(user);
}
}
In case I leave out the #Transactional annotation, the database doesn't get updated.
Well thats normal:
Every database manipulation in the CRUD Scheme needs it's transaction boundaries. Without those boundaries, nothing gets actually written in the DB.
A Transaction is a collection of DB Manipulations (inserts, Updates) whoich have all to be successfull or the whole action gets undone by the DB. That's why you have to tell Hibernate when a Transaction starts and ends, so hibernate can tell which actions have to be regardes as Units of Work. Without transaction boundaries the final commit to the database never happens.
hope that helped
Try to flush and commit.

Categories

Resources