How to bootstrap Hibernate without the persistence.xml file

Why?

JPA relies heavily on the persistence.xml configuration file, and the standard API to bootstrap a JPA provider programmatically requires too much boilerplate code. While in a typical enterprise application, providing a persistence.xml file is not really an issue, this requirement doesn’t get along with unit testing, especially when tests are completely isolated and they need to validate different aspects of JPA or Hibernate.

That was an issue that I bumped into when writing test cases for the High-Performance Java Persistence book. All my tests need to be isolated, and not all of them share the same settings or entities.

In my case, using a single persistence.xml file was definitely out of the question because any change would have a rippling effect through out the whole test suite.

Hibernate to the rescue

Hibernate is awesome. It allows you do build an EntityManagerFactory completely programmatically and with few lines of code:

protected EntityManagerFactory newEntityManagerFactory() {
    PersistenceUnitInfo persistenceUnitInfo = 
        persistenceUnitInfo(getClass().getSimpleName());
    Map<String, Object> configuration = new HashMap<>();
    configuration.put(AvailableSettings.INTERCEPTOR, 
        interceptor()
    );

    return new EntityManagerFactoryBuilderImpl(
            new PersistenceUnitInfoDescriptor(
                persistenceUnitInfo), configuration
    ).build();
}

protected PersistenceUnitInfoImpl persistenceUnitInfo(
    String name) {
    return new PersistenceUnitInfoImpl(
        name, entityClassNames(), properties()
    );
}

Each tests starts with some reasonable default properties, and entities must be provided on a per-test basis.

protected Properties properties() {
    Properties properties = new Properties();
    properties.put("hibernate.dialect", 
        dataSourceProvider().hibernateDialect()
    );
    properties.put("hibernate.hbm2ddl.auto", 
        "create-drop");
    DataSource dataSource = newDataSource();
    if (dataSource != null) {
        properties.put("hibernate.connection.datasource", 
        dataSource);
    }
    return properties;
}

protected List entityClassNames() {
    return Arrays.asList(entities())
        .stream()
        .map(Class::getName)
        .collect(Collectors.toList());
}

protected abstract Class[] entities();

A test can define its own settings and entities, and, this way, we can encapsulate the entire environment.

@Override
protected Class<?>[] entities() {
    return new Class<?>[] {
        Patch.class
    };
}

@Entity(name = "Patch")
public class Patch {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ElementCollection
    @CollectionTable(
            name="patch_change",
            joinColumns=@JoinColumn(name="patch_id")
    )
    @OrderColumn(name = "index_id")
    private List<Change> changes = new ArrayList<>();

    public List<Change> getChanges() {
        return changes;
    }
}

@Embeddable
public class Change {

    @Column(name = "path", nullable = false)
    private String path;

    @Column(name = "diff", nullable = false)
    private String diff;

    public Change() {
    }

    public Change(String path, String diff) {
        this.path = path;
        this.diff = diff;
    }

    public String getPath() {
        return path;
    }

    public String getDiff() {
        return diff;
    }
}

If you enjoyed this article, I bet you are going to love my book as well.

Conclusion

This technique is not something new. The Spring framework LocalContainerEntityManagerFactoryBean can also be configured without an actual peristence.xml file.

If you liked this article, you might want to subscribe to my newsletter too.

Advertisements

12 thoughts on “How to bootstrap Hibernate without the persistence.xml file

  1. As you might know, since you are using implementations of that interface, there is an SPI(http://docs.oracle.com/javaee/6/api/javax/persistence/spi/PersistenceUnitInfo.html) for bootstrapping JPA without a persistence.xml. It’s intended for containers, thus the name of the create method(http://docs.oracle.com/javaee/6/api/javax/persistence/spi/PersistenceProvider.html#createContainerEntityManagerFactory%28javax.persistence.spi.PersistenceUnitInfo,%20java.util.Map%29), but if you need to bootstrap that with custom settings, I would dare to say that counts as being container-like.
    This works for all JPA providers as that is the main integration point for all Java EE containers so it’s not really special to hibernate 😉

      1. Well it does work. I’ve done it a few times and Spring does it too. I don’t see how the hibernate API gives you “better control” although I agree it’s much more convenient to just use that instead of having to understand and implement the big PersistenceUnitInfo interface yourself.

  2. That has really been some of the biggest pain points for me with JPA (and other Java EE stuff). Why can’t I just set up my environment programmatically with only a couple of lines of code?

    Great share, thanks!

  3. Hi Vlad. This post is very interesting.

    I’m developing a Java EE 7 application that have to access to multiple datasources that share the same object structure (tables, sequences, stored procedures) and I think that the better way to add new datasources without the need of touch the persistence.xml file is through the method described here. In the shared code there a concrete class named PersistenceUnitInfoImpl that implements the PersistenceUnitInfo interface, ¿Do you have a code example with that implementation or can you refer me some Internet resource?

    Thanks.

      1. Thank’s vlad,

        I will review your class to give you the corresponding feedback.

        Very very thanks!!

      2. Hi Vlad.

        Sorry for the late answer. I implemented the corresponding components to the proposed model and my technical problem was fixed with success, I am thankful for your kind and valuety help.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s