Skip to content

v-ladynev/fluent-hibernate

Repository files navigation

fluent-hibernate

A library to work with Hibernate by fluent API. This library hasn't dependencies, except Hibernate libraries. It requires Java 1.6 and above. It can be used with Hibernate 5 and Hibernate 4.

Build Status

Download

Direct link

Release 0.3.1

Maven (pom.xml)
<dependency>
  <groupId>com.github.v-ladynev</groupId>
  <artifactId>fluent-hibernate-core</artifactId>
  <version>0.3.1</version>
</dependency>

All versions in the Maven repository: fluent-hibernate-core

Gradle (build.gradle)
compile 'com.github.v-ladynev:fluent-hibernate-core:0.3.1'

Hibernate 4 Notes

If you want to use the library with Hibernate 4, you can consider to exclude a transitive dependency to Hibernate 5. For example, using Gradle:

    compile('com.github.v-ladynev:fluent-hibernate-core:0.3.1') {
        exclude group: 'org.hibernate'
    }

The Most Useful Features

fluent-hibrnate has features which can be used with plain Hibernate or Spring code without a library infrastructure.

Scan the Class Path for Hibernate Entities

The library can be used for a quick entities scanning. You will need just the library jar, without additional dependencies. Just download the library using Download section and use EntityScanner:

For Hibernate 4 and Hibernate 5

Configuration configuration = new Configuration();
EntityScanner.scanPackages("my.com.entities", "my.com.other.entities")
    .addTo(configuration);
SessionFactory sessionFactory = configuration.buildSessionFactory();

Using a new Hibernate 5 bootstrapping API

List<Class<?>> classes = EntityScanner
        .scanPackages("my.com.entities", "my.com.other.entities").result();

MetadataSources metadataSources = new MetadataSources();
for (Class<?> annotatedClass : classes) {
    metadataSources.addAnnotatedClass(annotatedClass);
}

SessionFactory sessionFactory = metadataSources.buildMetadata()
    .buildSessionFactory();

Hibernate 5 Implicit Naming Strategy

It generates table and column names with underscores, like ImprovedNamingStrategy from Hibernate 4 and Hibernate 3, and constraint names (unique, foreign key) as well. Apart those, it has a lot of configurable interesting features like: plural table names, table and column prefixes, embedded column prefixes via the custom @FluentName annotation, automatic name restriction (by removing the vowels) and others.

Just download the library using Download section and use Hibernate5NamingStrategy:

Configuration configuration = new Configuration();
configuration.setImplicitNamingStrategy(new Hibernate5NamingStrategy());
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();

Using strategy options for a tables prefix and a names restriction except the join table names

Configuration configuration = new Configuration();
configuration.setImplicitNamingStrategy(
        new Hibernate5NamingStrategy(StrategyOptions.builder().tablePrefix("acps")
                .restrictLength(50).restrictJoinTableNames(false).build()));
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();

Using Spring

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  <property name="implicitNamingStrategy">
    <bean class="com.github.fluent.hibernate.cfg.strategy.hibernate5.Hibernate5NamingStrategy">
      <property name="options">
        <bean class="com.github.fluent.hibernate.cfg.strategy.StrategyOptions">
          <property name="tablePrefix" value="acps" />
          <property name="maxLength" value="50" />
          <property name="restrictJoinTableNames" value="false" />
        </bean>
      </property>
    </bean>
  </property>
</bean>

ImprovedNamingStrategy for Hibernate 5

It is imposible to use Hibernate 4 naming strategies, for an example org.hibernate.cfg.ImprovedNamingStrategy, with Hibernate 5 (HHH-10155). To continue using ImprovedNamingStrategy functionality with Hibernate 5 ImprovedNamingStrategyHibernate5 can be used. Apart an ImprovedNamingStrategy behaviour, ImprovedNamingStrategyHibernate5 provides generation of foreign key and unique constraint names like Hibernate 4 does (with some limitations, see below).

Just pass ImprovedNamingStrategyHibernate5 to the Hibernate 5 configuration. ImprovedNamingStrategyHibernate5 implements the ImplicitNamingStrategy interface, so you can place it everywhere ImplicitNamingStrategy can be placed.

For an example, using Configuration:

Configuration configuration = new Configuration();
configuration.setImplicitNamingStrategy(ImprovedNamingStrategyHibernate5.INSTANCE);
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();

ImprovedNamingStrategyHibernate5 can be used with JPA configuration as well.

Limitations of ImprovedNamingStrategy for Hibernate 5

Keep in mind, that ImprovedNamingStrategyHibernate5 has some limitations are described below. I think, that these limitations can be encountered in rarely situations. If you have problems with these situations fill free to create an issue or make a pull request.

I used ImprovedNamingStrategyHibernate5Test to test the strategy. To check an ImprovedNamingStrategy behaviour with Hibernate 4 I used this unit test: ImprovedNamingStrategyTest.

Constraint Names Generation

Foreign key and unique constraint names are generated by Hibernate 5 are slightly different from ones are generated by Hibernate 4.

I implement a Hibernate 4 behaviour in ImprovedNamingStrategyHibernate5, but Hibernate 5 doesn't always use a naming strategy to generate unique constraint names. For an, example: a unique constraint for @OneToMany annotation using a join table and @Column(unique = true) don't use ImplicitNamingStrategy. You need to delete such unique constraint names yourself, before Hibernate 5 will update a schema.

@DiscriminatorColumn Name Generation

Hibernate 5 doesn't use a naming strategy to generate such names, because of an issue. Hiibernate 5 generates DTYPE name for the discriminator column. Hibernate 4 uses ImprovedNamingStrategy#columnName() to convert DTYPE to dtype. You need to provide explicit names in such situation.

@OrderColumn Name Generation

Hibernate 5 doesn't use a naming strategy to generate such names, because of an issue. Hibernate 5 generates booksOrdered_ORDER, opposite books_ordered_order is generated by Hibernate 4 ImprovedNamingStrategy. You need to provide explicit names in such situation.

@MapKeyColumn Name Generation

Hibernate 5 doesn't use a naming strategy to generate such names, because of an issue. Hibernate 5 generates booksMap_KEY, opposite books_map_key is generated by Hibernate 4 ImprovedNamingStrategy. You need to provide explicit names in such situation.

Other Artifacts Name Generation

There are some methods in the ImplicitNamingStrategy are not used by Hibernate 5 to generate names. And there are some methods for which I can't represent unit tests. All these methods throw UnsupportedOperationException. The list of methods below

    Identifier determineTenantIdColumnName(ImplicitTenantIdColumnNameSource source);

    Identifier determineIdentifierColumnName(ImplicitIdentifierColumnNameSource source);

    Identifier determinePrimaryKeyJoinColumnName(ImplicitPrimaryKeyJoinColumnNameSource source);

    Identifier determineAnyDiscriminatorColumnName(ImplicitAnyDiscriminatorColumnNameSource source);

    Identifier determineAnyKeyColumnName(ImplicitAnyKeyColumnNameSource source);

Hibernate4To5NamingStrategyAdapter

This adapter can be used to adapt Hibernate 4 naming strategies to Hibernate 5 ImplicitNamingStrategy. ImprovedNamingStrategy for Hibernate 5 is builded on top of it. So it has the same limitations.

You can use the adapter by this way:

Configuration configuration = new Configuration();
configuration.setImplicitNamingStrategy(new Hibernate4To5NamingStrategyAdapter(EJB3NamingStrategy.INSTANCE));
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();

It can be used with JPA configuration as well. Probably, you will need to extend Hibernate4To5NamingStrategyAdapter similar as ImprovedNamingStrategyHibernate5 extends it.

Nested Transformer

It is a custom transformer like Transformers.aliasToBean(SomeDto.class), but with the nested projections support.

Just download the library using Download section and use FluentHibernateResultTransformer.

Load User only with login and department.name fields

@Entity
@Table(name = "users")
public class User {

    @Id
    @Column(name = "pid")
    private Long pid;

    @Column(name = "login")
    private String login;

    @ManyToOne
    @JoinColumn(name = "fk_department")
    private Department department;

}

@Entity
@Table(name = "departments")
public class Department {

    @Id
    @Column(name = "pid")
    private Long pid;

    @Column(name = "name")
    private String name;

}

Criteria criteria = session.createCriteria(User.class, "u");
criteria.createAlias("u.department", "d", JoinType.LEFT_OUTER_JOIN);
criteria.setProjection(Projections.projectionList()
        .add(Projections.property("u.login").as("login"))
        .add(Projections.property("d.name").as("department.name")));

List<User> users = criteria
        .setResultTransformer(new FluentHibernateResultTransformer(User.class))
        .list();

Please, don't forget to specify projection aliases.

Using With Native SQL

It is impossible with Hibernate 5 to use Transformers.aliasToBean(SomeDto.class) the same way as it is used with Hibernate 3 — without the aliases with the quotes (a more deep explanation), but it is possible using FluentHibernateResultTransformer. This code works pretty well:

List<User> users = session.createSQLQuery("select login from users")
        .setResultTransformer(new FluentHibernateResultTransformer(User.class))
        .list();

The transformer can be used for a native SQL with the nested projections (opposite HQL). It is need to use the aliases with the quotes in this case:

String sql = "select u.login, d.name as \"department.name\" "
        + "from users u left outer join departments d on u.fk_department = d.pid";
List<User> users = session.createSQLQuery(sql)
        .setResultTransformer(new FluentHibernateResultTransformer(User.class))
        .list();

Ignore Alias Duplicates

Sometimes it is convenient using complex search criteries to add an alias multiple times. In such situations Hibernate generates the org.hibernate.QueryException: duplicate alias exception . There is a utility class to solve this problem: Aliases. This code with alias duplicates working without errors:

Criteria criteria = session.createCriteria(User.class, "u");
Aliases aliases = Aliases.create()
        .add("u.department", "d", JoinType.LEFT_OUTER_JOIN)
        .add("u.department", "d", JoinType.LEFT_OUTER_JOIN);

criteria.add(Restrictions.isNotNull("d.name"));
aliases.addToCriteria(criteria);
List<User> users = criteria.list();

Examples

Get all users

List<User> users = H.<User> request(User.class).list();

Getting a user with a login my_login

final String loginToFind = "my_login";
User user = H.<User> request(User.class).eq("login", loginToFind).first();

A partial objects loading

Get all users, but only with login and id properties are filled (other properties will be null).

List<User> users = H.<User> request(User.class).proj("login").proj("id")
    .transform(User.class).list();

Example Projects

Contributors

Dependency Status

Dependency Status

Progress

Throughput Graph

TODO:

Stories in Ready In Progress