Install
To use the latest snapshot jpersis-{version} add the following dependency to your project:
<dependency>
<artifactId>jpersis</artifactId>
<groupId>com.github.myrealitycoding</groupId>
<version>{version}</version>
</dependency>
Getting Started
Table of contents
I. Why JPersis?
II. Models
III. Mappers
IV. Example
V. Drivers
VI. Annotations
VII. Naming
VIII. Modes
IX. References
I. Why JPersis?
When using an object oriented language like Java it is time consuming to map data manually from a database to models and vise versa. You have to remember data types, validate input and output and you have to change your code for each use case. JPersis is a light-weighted library which does not require much configuration. You define a datastore, bind a model to it and use so called mappers to interact with the datastore.
II. Models
A model is a simple Java class with attributes. There are some restrictions to fullfill the role:
- each model has to annotate one and only one field by a
@PrimaryKey
annotation - except
Date
no other classes are allowed as field data types - if a model has a custom field data type, it has to be annotated by
@Ignored
- a model needs a public constructor without arguments
Imagine we have a model, called User
, it could look something like this:
public class User {
@PrimaryKey
private int id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
You have created a valid JPersis model, because all requirements are fullfilled.
III. Mappers
Now we need to do something with our model. We want to store, read and manipulate data. Each model needs a mapper which is responsible. To implement a mapper you have the following restrictions:
- each mapper has to be annotated by a
@Mapper
annotation which also provides a correct class path to the model - each mapper has to be an interface
- each method of the mapper needs to be annotated by a supported annotation (See VI. Annotations).
A sample mapper for our previously created model could look something like this:
@Mapper("my.package.to.the.model.User")
public interface UserMapper {
@Insert
boolean insert(User user);
@Delete
boolean delete(User user);
@Count
int count();
@Select(condition = "userName = $1")
User findByUserName(String userName);
}
That's it! You have created your first mapper.
IV. Example
Now we want some action. We want to use a SQLite datasource. We create a driver first (provided by JPersis) and create an JPersis
object:
Driver driver = new SQLiteDriver("database.sql");
JPersis jpersis = new JPersis(driver);
Now our application is ready to use. Let's create a user first:
User user = new User();
user.setName("Max");
Next we need our mapper. JPersis will provide a mapper for you:
UserMapper mapper = jpersis.map(UserMapper.class);
Let's insert our user and check if it worked:
mapper.insert(user); // returns true, seems to be okay!
mapper.count(); // returns 1, there is a user in the database
mapper.findByName("Max"); // Hey Max, how are you doing?
Nothing more to do. JPersis created a user table internally and inserted the user into it. If we want to avoid custom mappers, we can fall back to default mapping:
DefaultMapper<User> mapper = jpersis.mapDefault(User.class);
Collection<User> allUsers = mapper.find();
// We do not want to query always the datastore.
// Instead we are using caching
// Equivalent to:
// CachedDefaultMapper<User> cachedMapper = jpersis.mapDefaultCached(User.class);
CachedDefaultMapper<User> cachedMapper = jpersis.cached(mapper);
V. Drivers
To interact with the database, a so called Driver is required. A driver maps a general functionality to a technology based functionality (like a general insert command to an MongoDB insert). Furthermore a driver is used by annotation methods.
By default, the following drivers will be supported:
- SQLiteDriver
- MySQLDriver
- PostgreSQLDriver
- CassandraDriver (not implemented yet)
- Neo4jDriver (not implemented yet)
- MongoDriver (not implemented yet)
VI. Annotations
The annotations provided by Jpersis are predefined but it is possible to register custom annotations to Jpersis. Given the following annotation:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReturnOne { }
Annotating a method with this annotation should always return for example the value 1
. To do so we need to define a so called MapperMethod
:
public class ReturnOneMethod extends AbstractMapperMethod<ReturnOne> {
public ReturnOneMethod(ReturnOne one) {
super(one);
}
@Override
public void on(Class<?> model, Object[] params, Query query) {
// we build our query internally to count one element
// note that this would return 0 if no data is available
query.limit(1).count();
}
@Override
protected Class<?>[] supportedReturnTypes(Class<?> model) {
return new Class<?>[] { Integer.class, int.class };
}
}
Finally we need to register our mapper method:
jpersis.register(ReturnOne.class, ReturnOneMethod.class);
We are now able to annotate mappers with the @ReturnOne
annotation:
public interface MyMapper {
/* ... */
@ReturnOne
int returnOneOrZero();
/* ... */
}
VII. Naming
It is also possible to customize conversion between Java and database names. To do so, you have to define a Naming
. By default, the CamelCaseNaming
is used:
myField -> my_field
my_field -> myField
MyClass -> my_collection
my_collection -> MyCollection
To do so, implement your own converter:
public class CustomNaming implements Naming {
@Override
public String collectionToJava(String name) {
return name;
}
@Override
public String javaToCollection(String name) {
return name;
}
@Override
public String fieldToJava(String name) {
return name;
}
@Override
public String javaToField(String name) {
return name;
}
}
Afterwards you can set your converter:
jpersis.setNaming(new CustomNaming());
Modes
JPersis currently supports two modes:
- CUSTOM This mode allows custom connections which need to be established manually in the driver.
- AUTO By default, JPersis automatically establishes communication with the datastore when required.
The mode itself can be changed in the driver:
Driver driver = retrieveDriver();
driver.setMode(DriverMode.CUSTOM);
References
As of Version 1.1.0 JPersis does support references:
public class CarPark {
@PrimaryKey
private String id;
@Reference("de.test.Car")
private Collection<Car> cars;
}
This will give you all cars which are referenced to this object. For more information refer to the actual WIP ticket.