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))
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>
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());
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);
- the
afterInsert()
identifier policy means that the country.id column is an auto-increment one. Two other policies exist: thebeforeInsert()
for identifier given by a database Sequence (for example), and thealreadyAssigned()
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 thePersistenceContext
dataSource):
DDLDeployer ddlDeployer = new DDLDeployer(persistenceContext);
ddlDeployer.deployDDL();
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);
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);
}
Then you can declare your repository as such, to be injected into your services :
@Repository
public interface CountryStalactiteRepository extends StalactiteRepository<Country, Long> {
}
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)