{% extends "base.html" %} {% block title %}Getting started guide{% endblock %} {% block body %}
Using siena is very easy. You just create your model classes and start to use them. The only configuration is the annotation in your clases and a simple properties file. The model classes must follow some constraints:
siena.Model
. See the examples bellow for more information.all()
method like in the example. Nevertheless this is an optional step.@Table
annotation. The meaning of this annotation
will depend on the underlying siena implementation. This will be the table
name in a relational database, the entity name in the Google App Engine datastore,
the domain name in Amazon's SimpleDB,... The annotation is just named table
because is an easy to recognize nomenclature.@Column
annotations to the fields. This is very similar
to the @Table
annotation. The meaning of the annotation value
will depend on the underlying siena implementation. @Id
annotation. Some keys can be generated
automatically by the application or the database. See the example.import siena.*; import static siena.Json.*; @Table("employees") public class Employee extends Model { @Id(Generator.AUTO_INCREMENT) public Long id; @Column("first_name") @Max(200) @NotNull public String firstName; @Column("last_name") @Max(200) @NotNull public String lastName; @Column("contact_info") public Json contactInfo; @Column("boss") @Index("boss_index") public Employee boss; @Filter("boss") public Query<Employee> employees; public static Query<Employee> all() { return Model.all(Employee.class); } }
Now you just need to write a simple configuration file. In siena all the classes in the same package
are configured with the same PersistenceManager
. The configuration file must be
called siena.properties
and must be placed in the same package than the model classes.
The parameters in this configuration file depend on the siena implementation. The only shared configuration parameter is the implementation parameter. With that parameter you set what siena implementation will be used for the classes in that pacakge. Example:
implementation = siena.jdbc.JdbcPersistenceManager driver = com.mysql.jdbc.Driver url = jdbc:mysql://localhost/siena-example user = root password = 1234
The example has configured the siena-jdbc implementation. As you can see the siena-jdbc implementation requires some other configuration parameters. You can learn more about each siena implementation in the specific documentation for each implementation.
Now that your model is created and configured you can start using siena.
First of all an example:
List<Employee> employees = Employee.all() .filter("firstName", "Mark") .order("-lastName") .fetch(10);
The all()
method is a good starting point for executing queries.
That method returns a Query
object that is a representation of
a query. The Query
interface has four main methods:
filter()
, order()
, fetch()
and
get()
.
filter()
methodThis method puts restrictions to the query.
It requires two parameters: the field name (optionally
with an operator) and the restricted value. If no opeartor is specified
then "=" is assumed. You can use other operators: <, >, <= or >=. You
can call filter()
several times. The query will only return
those objects that match all restrictions. There is no way to specify
that the query will return objects that match some restrictions or others.
Comparing to SQL the filter()
method is like an AND
operator in a WHERE
clause and there is no support for
a OR
operator.
order()
methodThis is the method you will use for sorting. It requires one parameter that is the name of the field that will be used for sorting. You can concatenate a "-" before the field name for descending sort. You can call this method several times.
fetch()
methodThis method will return a list of objects that match the given
constraints sorted by the given fields. There are three versions
of this method to implement pagination. If you pass no arguments
all the objects that match the constraints will be returned.
Be careful with that because if you have several objects stored.
You can limit the maximum size of returned objects with the first
argument. And optionally you can define an offset
as second argument.
get()
methodIf you just want the first result of the query you can use
get()
. This method will return null
if there query returns no objects.
It is important to note that nothing is really queried until you call
fetch()
or get()
. The methods
all()
, filter()
and order()
just prepare the query but they don't execute it.
The Siena API is very easy. If your model classes inherith the methods
of the siena.Model
class. These methods will let
you insert, update, delete or load single objects. If you don't want
to use inheritance because you can't or you want to follow the POJO
philosophy you can use the methods of the PersistenceManager
class instead of using the methods of the siena.Model
class.
For example:
Employee e = getSomeEmployee(); // retrieve the configured PersistenceManager: PersistenceManager pm = PersistenceManagerFactory.getPersistenceManager(Employee.class); // the following two lines are equivalent: pm.update(e); // you will use it if you don't extend siena.Model e.update(); // if Employee extends siena.Model
Note: all the examples bellow use the siena.Model
methods but you will
find equivalent methods in the PersistenceManager
class.
If you have the primary keys of an object and you want to load
all its fields you just need to create an empty object,
put the primary key field values and call get()
.
Employee e = new Employee(); e.id = 123; // we know the primary keys e.get(); // this loads the object System.out.println(e.firstName);
If no object is found with those primary keys then a SienaException
will be thrown.
If you don't want to handle exceptions you can use the Query
interface.
Example:
Employee e = Employee.all().filter("id", 123).get(); System.out.println(e.firstName);
This way you will get null
instead of an exception if there is no such object.
You can even create your own static method:
public static Employee get(long id) { return Employee.all().filter("id", 123).get(); }
Inserting an object is very simple. Just load the fields with the appropiate values
and then call insert()
. If your object has some generated primary keys
you will be able to get them just after inserting the object.
Employee e = new Employee(); e.firstName = "John"; e.lastName = "Smith"; e.insert(); System.out.println(e.id); // the generated key is available
The update()
method lets you update an object in the persistence storage.
Employee e = Employee.get(123); e.firstName = "Mark"; e.update();
Finally you can delete an object using the delete()
method.
Employee e = Employee.get(123); e.delete();
The delete()
method only needs the primary keys to be loaded. So if you know
them you don't even need to execute a query to previously load the object.
In order to create a one-to-many relationship you just need to create a reference from the child class to the parent class. Example:
public class Pet extends Model { @Column("owner") public Person owner; // each pet has an owner // more fields }
It is strongly recommended to use the @Column
annotation when declaring relationships
if you are using sinea-jdbc.
To fetch all the pets of a given person you can just query filtering by that field.
Person p = somePerson(); Query<Pet> pets = Pet.all().filter("owner", p); List<Pet> somePets = pets.fetch(10);
Or you can also create an automatic-query in the Person
class.
public class Person extends Model { @Filter("owner") public Query<Pet> pets; // this is called an "automatic-query" // more fields }
Person p = somePerson(); List<Pet> somePets = p.pets.fetch(10);
As you can see the @Filter
annotation tells which field must be used to query against the
Pet
class.
In the first example at this page you can see that a Json
field has been used to store complex
data structures into one field. Well, there is another way to store complex data strctures into
one field: using embedded objects. Suppose you have a web page whose users have "profile images". Each image
has a filename, a title and a counter that counts how many times the image has been displayed.
public class User extends Model { @Embedded public List<Image> profileImages; // more fields }
You just need to put the @Embedded
annotation to the field that will store the embedded data.
This field can be an object, a java.util.List
or a java.util.Map
.
However the sotored objects must be of a class properly annotated. Let's see how:
@EmbeddedMap public class Image { public String filename; public String title; public int views; }
You have annotate your embeddec classes either with @EmbedMap
or @EmbedList
.
The embedded object will be serialized into JSON when inserted in the database. An example of
how the profileImages
field could be serialized:
[{"title": "Example 1", "views": 2, "filename": "1.jpg"}, {"title": "Example 2", "views": 20, "filename": "2.jpg"}]
When using @EmbedList
the object will be serialized into a JSON list. In this case
you must annotate the fields using @At
. This is an example:
@EmbeddedList public class Image { @At(0) public String filename; @At(1) public String title; @At(2) public int views; }
The fields must be in order as well. This is how the object would be serialized:
[["1.jpg", "Example 1", 2], ["2.jpg", "Example 2", 20]]
The JSON result is shorter but harder to understand.
The "embedded objects" feature is very powerful because you can nest embedded objects into other embedded
objects. So for example the Image
class could have other objects nested or other lists or maps
inside it. This feature also has de avantaje of knowing the structure of the data at compile time.