TUT - Hibernate, JPA configuration

Hibernate is a nice framework which can save lot of time, espetially if you want to provide support for multiple relational databases or you will need to write lots native of queries.

JPA - stands for Java Persistence API
- the api is defined in javax.persistence package’
- jpa is only specification
Hibernate - is orm framework, which implements JPA

What we will need:

    <dependency>
        <groupId>org.hibernate.javax.persistence</groupId>
        <artifactId>hibernate-jpa-2.1-api</artifactId>
        <version>1.0.0.Final</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.3.10.Final</version>
        <scope>compile</scope>
    </dependency>

persistence.xml in your meta inf folder

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="YOUR_UNIT_NAME" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <properties>
        <property name="hibernate.connection.url"  value="dburl"/>
        <property name="hibernate.connection.password" value="pw"/>
        <property name="hibernate.connection.driver_class" value="driver"/>
        <property name="hibernate.connection.user" value="username"/>
        <property name="hibernate.dialect" value="dialect"/>
    </properties>
</persistence-unit>

Because the persistence.xml must be inside your jar, is better to override these properties from external file.
Lets create file database.properties in your resources folder.

hibernate.connection.url = jdbc:postgresql://localhost:5432/spongeplugin
hibernate.connection.password = 123456
hibernate.connection.driver_class = org.postgresql.Driver
hibernate.connection.user = postgres
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
hibernate.hbm2ddl.auto = validate
  • hibernate.connection.url - jdbc url to your database
  • hibernate.connection.driver_class - classpath to a database driver, the driver must be in a classpath
  • hibernate.dialect.dialect - specifies language, every database has its own.
  • hbm2ddl - this property allows hibernate to work with ddl.
    • possible values:
      - update: updates the schema.
      • validate: validates the schema.
      • create: creates the schema and destroys old data.
      • create-drop: drop the schema at the end of the session

During server startup copy this file out of the jar

path = Paths.get(pluginDir+File.separator+"database.properties");
    if (!Files.exists(path, LinkOption.NOFOLLOW_LINKS)) {
        InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("database.properties");
        try {
            Files.copy(resourceAsStream,path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Load configuration.

    Properties properties = new Properties();
    try (FileInputStream stream = new FileInputStream(p.toFile())){
        properties.load(stream);
    } catch (IOException e) {
        e.printStackTrace();
    }

Sometimes you have to load the driver class manually.

    try {
        Class.forName(properties.getProperty("hibernate.connection.driver_class"));
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

Now we have to create instance of EntityManager. EntityManager is the jpa interface with methods for interacting with persistent context.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("YOUR_UNIT_NAME", properties);
EntityManager em = emf.createEntityManager();

If you did everything right you should see similar log in console, it looks ugly but dont be scared.

    vc 22, 2015 3:21:01 DOP. org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvi
der [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePe
rsistenceProvider] instead.
vc 22, 2015 3:21:01 DOP. org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvi
der [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePe
rsistenceProvider] instead.
vc 22, 2015 3:21:01 DOP. org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvi
der [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePe
rsistenceProvider] instead.
vc 22, 2015 3:21:01 DOP. org.hibernate.jpa.internal.util.LogHelper logPersisten
ceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
        name: ntrpg
        ...]
vc 22, 2015 3:21:02 DOP. org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.10.Final}
vc 22, 2015 3:21:02 DOP. org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
vc 22, 2015 3:21:02 DOP. org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
vc 22, 2015 3:21:03 DOP. org.hibernate.annotations.common.reflection.java.JavaR
eflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
vc 22, 2015 3:21:03 DOP. org.hibernate.engine.jdbc.connections.internal.DriverM
anagerConnectionProviderImpl configure
WARN: HHH000402: Using Hibernate built-in connection pool (not for production us
e!)
vc 22, 2015 3:21:03 DOP. org.hibernate.engine.jdbc.connections.internal.DriverM
anagerConnectionProviderImpl buildCreator
INFO: HHH000401: using driver [org.postgresql.Driver] at URL [jdbc:postgresql://
localhost:5432/project]
vc 22, 2015 3:21:03 DOP. org.hibernate.engine.jdbc.connections.internal.DriverM
anagerConnectionProviderImpl buildCreator
INFO: HHH000046: Connection properties: {user=postgres, password=****}
vc 22, 2015 3:21:03 DOP. org.hibernate.engine.jdbc.connections.internal.DriverM
anagerConnectionProviderImpl buildCreator
INFO: HHH000006: Autocommit mode: false
vc 22, 2015 3:21:03 DOP. org.hibernate.engine.jdbc.connections.internal.DriverM
anagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
vc 22, 2015 3:21:04 DOP. org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
vc 22, 2015 3:21:04 DOP. org.hibernate.engine.jdbc.internal.LobCreatorBuilder u
seContextualLobCreation
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw
error : java.lang.reflect.InvocationTargetException
╚vc 22, 2015 3:21:04 DOP. org.hibernate.hql.internal.ast.ASTQueryTranslatorFacto
ry <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory

Once you are working with a tool like this its very good pratice to refactor your code to Pojo class (plain old java object) this object should not inherit, and should have only getters and setters, Dao - data access object, is an object which provides access to underlying database, service - provides main logic

Now you have to create an entity

@Entity
public class CharacterBase {

@Id /*primary key */
@GeneratedValue(strategy = GenerationType.IDENTITY) /* auto increment */
private Long id;

private String name;

private String info;
 + getters, setters 

each entity has its own table in database. jpa scans your project for all entities and autimatically creating tables.
Each property has its own column in table, unless the property is annotated with @Transient, then is ignored. JPA can handle all types of relations (1:m, n:m … ), indexing all you have to do is read the docs (…ouch)

You can annotate fields, or methods but not both, i prefer fields,the code looks simpler.

And finally simple example how to use entitymanager and load or save data to database

    em.getTransaction().begin();
     CharacterBase cb1 = new CharacterBase();
     cb1.setName("first");
     em.persist(cb1);
     CharacterBase cb2 = new CharacterBase();
     cb2.setName("first");
     em.persist(cb2);
     em.getTransaction().commit();

    //transactions are not needed for select queries

     TypedQuery<CharacterBase> query = em.createQuery("SELECT a FROM CharacterBase a WHERE a.name=:name",CharacterBase.class);
        query.setParameter("name","first");
        List<CharacterBase> list = query.getResultList();

        em.getTransaction().begin();
        list.get(0).setName("second");
        em.merge(list.get(0));
        em.getTransaction().commit();

Dont forget that reading from stream is thread blocking task, and its bad idea to call queries in minecraft’s main thread.

Its questionable if you should include all dependencies into your pluginjar, if you place the jars into your mods directory forge can add them into classpath automaticaly. Size of all libraries is around 7mb.

3 Likes