DEV Community

Cover image for Introducing Stalactite ORM
Guillaume Mary for Codefilarete

Posted on

Introducing Stalactite ORM

Java ORM world is very steady and few libraries exist, but none of them brought any breaking change over the last decade. Meanwhile, application architecture evolved with some trends such as Hexagonal Architecture, CQRS, Domain Driven Design, or Domain Purity. Stalactite tries to be more suitable to these new paradigms by allowing to persist any kind of Class without the need to annotate them or use external XML files: its mapping is made of method reference.

As a benefit, you get a better view of the entity graph since the mapping is made through a fluent API that chains your entity relations, instead of spreading annotations all over entities. This is very helpful to see the complexity of your entity graph, which would impact its load as well as the memory. Moreover, since Stalactite only fetches data eagerly, we can say that what you see is what you get. Here is a very small example:

MappingEase.entityBuilder(Country.class, Long.class)
    .mapKey(Country::getId, IdentifierPolicy.afterInsert())
    .mapOneToOne(Country::getCapital, MappingEase.entityBuilder(City.class, Long.class)
        .mapKey(City::getId, IdentifierPolicy.afterInsert())
        .map(City::getName))
Enter fullscreen mode Exit fullscreen mode

First Steps

The release 2.0.0 is out for some weeks and is available as a Maven dependency, hereafter is an example with HSQLDB. For now, Stalactite is compatible with the following databases (mainly in their latest version): HSQLDB, H2, PostgreSQL, MySQL, and MariaDB.

<dependency>
   <groupId>org.codefilarete.stalactite</groupId>
   <artifactId>orm-hsqldb-adapter</artifactId>
   <version>2.0.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

If you're interested in a less database-vendor-dedicated module, you can use the orm-all-adapter module. Just be aware that it will bring you extra modules and extra JDBC drivers, heaving your artifact.

After getting Statactite as a dependency, the next step is to have a JDBC DataSource and pass it to a org.codefilarete.stalactite.engine.PersistenceContext:

org.hsqldb.jdbc.JDBCDataSource dataSource= new org.hsqldb.jdbc.JDBCDataSource();
dataSource.setUrl("jdbc:hsqldb:mem:test");
dataSource.setUser("sa");
dataSource.setPassword("");
PersistenceContext persistenceContext = new PersistenceContext(dataSource, new HSQLDBDialect());
Enter fullscreen mode Exit fullscreen mode

Then comes the interesting part: the mapping. Supposing you get a Country, you can quickly set up its mapping through the Fluent API, starting with the org.codefilarete.stalactite.mapping.MappingEase class as such:

EntityPersister<Country, Long> countryPersister = MappingEase.entityBuilder(Country.class, Long.class)
    .mapKey(Country::getId, IdentifierPolicy.afterInsert())
    .map(Country::getName)
    .build(persistenceContext);
Enter fullscreen mode Exit fullscreen mode
  • the afterInsert() identifier policy means that the country.id column is an auto-increment one. Two other policies exist: the beforeInsert()for identifier given by a database Sequence (for example), and the alreadyAssigned() for entities that have a natural identifier given by business rules,
  • any non-declared property is considered transient and not managed by Stalactite. The schema can be generated with the org.codefilarete.stalactite.sql.ddl.DDLDeployer class as such (it will generate it into the PersistenceContext dataSource):
DDLDeployer ddlDeployer = new DDLDeployer(persistenceContext);
ddlDeployer.deployDDL();
Enter fullscreen mode Exit fullscreen mode

Finally, you can persist your entities thanks to the EntityPersister obtained previously, please find the example below. You might notice that you won't find JPA methods in Stalactite persister. The reason is that Stalactite is far different from JPA and doesn't aim at being compatible with it: no annotation, no attach/detach mechanism, no first-level cache, no lazy loading, and many more. Hence, the methods are quite straight to their goal:

Country myCountry = new Country();
myCountry.setName("myCountry"); 
countryPersister.insert(myCountry); 
myCountry.setName("myCountry with a different name"); 
countryPersister.update(myCountry); 
Country loadedCountry = countryPersister.select(myCountry.getId()); 
countryPersister.delete(loadedCountry);
Enter fullscreen mode Exit fullscreen mode

Spring Integration

There was a raw usage of Stalactite, meanwhile, you may be interested in its integration with Spring to benefit from the magic of its @Repository. Stalactite provides it, just be aware that it's still a work-in-progress feature. The approach to activate it is the same as for JPA: enable Stalactite repositories thanks to the @EnableStalactiteRepositories annotation on your Spring application. Then you'll declare the PersistenceContext and EntityPersister as @Bean :

@Bean
public PersistenceContext persistenceContext(DataSource dataSource) {
    return new PersistenceContext(dataSource);
}

@Bean
public EntityPersister<Country, Long> countryPersister(PersistenceContext persistenceContext) { 
    return MappingEase.entityBuilder(Country.class, long.class) 
            .mapKey(Country::getId, IdentifierPolicy.afterInsert())
            .map(Country::getName)
            .build(persistenceContext);
}
Enter fullscreen mode Exit fullscreen mode

Then you can declare your repository as such, to be injected into your services :

@Repository
public interface CountryStalactiteRepository extends StalactiteRepository<Country, Long> {
}
Enter fullscreen mode Exit fullscreen mode

As mentioned earlier, since the paradigm of Stalactite is not the same as JPA (no annotation, no attach/detach mechanism, etc), you won't find the same methods of JPA repository in Stalactite ones :

  • save : Saves the given entity, either inserting it or updating it according to its persistence states
  • saveAll : Same as the previous one, with a massive API
  • findById : Try to find an entity by its id in the database
  • findAllById : Same as the previous one, with a massive API
  • delete : Delete the given entity from the database
  • deleteAll : Same as the previous one, with a massive API

Conclusion

In these chapters we introduced the Stalactite ORM, more information about the configuration, the mapping, and all the documentation are available on the website.

The project is open-source with the MIT license and shared through Github.

Thanks for reading, any feedback is appreciated!

Top comments (0)