poniedziałek, 29 grudnia 2014

Hibernate annotations: many-to-one with custom column

Sometimes you need to create many to one relation that doesn't use primary key. For example:

|Foo|            |Bar|            
 ---------        -----------
|id (PK)  |      |id (PK)    |
 ---------        -----------
|bar_id   |----->|bar_id (UQ)|
 ---------        -----------
                 |bar_name   |
here Foo references Bar by bar_id not id (id is more like a record's id not Bar's id)...What you need to do is annotate attribute like that (this is Foo class):

    @JoinColumn(name="bar_id", referencedColumnName = "bar_id", nullable = false)
    private Bar bar;

If you want to have access to list of Foos from Bar you make something like this:

    @JoinColumn(name="bar_id", referencedColumnName = "bar_id")
    private Set<Foo> foos = new HashSet<>();

One more thing...if you have

<property name="hibernate.hbm2ddl.auto">create</property>

or "create-drop" you will probably get an error and there will be no foreign key added to table Foo. What you need to do is stop app change that prop:

<property name="hibernate.hbm2ddl.auto">update</property>

and start app again. This time there should be foreign key added to table. That's probably some sort of Hibernate bug.

TDD with Spring3 + Hibernate + Transactions

I found many people, including myself, struggling with creating test environment for web apps using Spring, Hibernate and Transactions. Sometimes there's a need to test your services in action without mock objects and so on. So how do you do that?

Here's main configuration class:

import java.util.Properties;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
 * @author MarcinG
@ComponentScan(basePackages = {})
public class DataTestConfig {

    public SessionFactory sessionFactory() {
        LocalSessionFactoryBuilder builder
                = new LocalSessionFactoryBuilder(dataSource());


        return builder.buildSessionFactory();

    private Properties getHibernateProperties() {
        Properties prop = new Properties();
        prop.put("hibernate.format_sql", "true");
        prop.put("hibernate.show_sql", "true");
        prop.put("hibernate.hbm2ddl.auto", "create-drop");
        return prop;

    @Bean(name = "dataSource")
    public DriverManagerDataSource dataSource() {

        DriverManagerDataSource ds = new DriverManagerDataSource();
        return ds;

    public HibernateTransactionManager transactionManager() {
        return new HibernateTransactionManager(sessionFactory());
In my case I'm using Gradle, so I have file "hibernate-test.cfg.xml" inside Resources dir. It looks like that:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  <property name="hibernate.connection.pool_size">10</property>
  <property name="hibernate.connection.autocommit">true</property>
  <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
  <property name="hibernate.hbm2ddl.auto">create-drop</property><!-- creates the tables from the entites automatically -->
  <property name="show_sql">true</property>
  <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
  <mapping resource="pl/com/marcing/test/hibernate/Test.hbm.xml"/>

Now you create tests like this one:

 * @author Gensiub
@ContextConfiguration(classes = {DataTestConfig.class})
public class TestFieldInRaportService {
    TestService testService;

    /* service tests */ 
That service has @Transactional annotation. It uses DAO and so on. Test env will have in memory database which will work like a normal one. You can test whether you can retrieve previously saved objects and so on (no need for normal database).