Skip to content

Latest commit

 

History

History
319 lines (228 loc) · 11.4 KB

relation_syntax_many.textile

File metadata and controls

319 lines (228 loc) · 11.4 KB

This syntax is only available for GAE implementation for the time being but will be ported soon to other impl

The following code samples can be found in samples dir

Description

  • Many<T> represents the relation itself from the owner/aggregator entity to the owned or aggregated children entities in a One2Many/Many2One relation.
  • Many<T> is set on the owner side in a multiple owned relation.
  • Many<T> is set on the aggregator side in a multiple aggregated relation.
  • Many<T> provides a lazy way to retrieve the children of the relation/aggregation as a List or as a Query.

Note with Many<T>, you don’t have to choose in your model how you want to manipulate your child entities: you will be able to do it through a simple list retrieved at once or through a query with limits, offsets, filters, ordering etc…

Keep in mind that Siena just manages the relation but not the objects themselves

Siena DOES

  • manage the relation but not the full life-cycle of related entities (you must manage their updates for ex).
  • manage relation change when you add a child to the One2Many relation.
  • auto-insert the related entities at the same time as the parent (or when updating the parent) if not inserted yet.
  • lazy-load the related entities only when required.
  • delete-cascade the aggregated entities because an aggregated entity can’t exist outside its aggregator. This is the ONLY accepted delete cascade.

Siena DOESN’T

  • manage the updates of data within the related entities. You must save them yourself.
  • delete the owned entities when you delete the owner since an owned entity can change owner. Nevertheless, the owned entity is no more associated to any owner if you delete the owner.
public class OwnerModel {
...
	@Owned(mappedBy="owner")
	// @Owned 
	// is also valid: the mappingBy is not mandatory and if not precised, siena will find the first field having OwnerModel type
	public Many<ChildModel> child;	
...
}

Here is the child model:

public class ChildModel {
...
	public OwnerModel owner;	
...
}

Note On the child side, the owner field is just written as a simple reference to the owner without any One<T>. Why? because, on the owned entity side, the owner is just referenced by its id/key.

Note On the owner side, the @Owned.mappedBy is used to specify which field in the child maps the owner. But the mappedBy is not mandatory and Siena can deduce the field by itself.

Using the old notation Query<T>, the Many<T> is equivalent to:

public class OwnerModel {
...
	@Filter("owner")
	public Query<ChildModel> child;	
...
}


Here is the aggregator model:

public class AggregatorModel {
...
	@Aggregated
	public Many<ChildModel> child;	
...
}

Here is the aggregated model:

public class AggregatedModel {
...
...
}

Note On the aggregated side, there is no reference to the aggregator because an aggregated entity shall not be aware of its aggregator: it’s a part of it but it was not necessarily meant to be.
It’s very useful because you don’t need to foresee your model will be aggregated while designing it and you can aggregate any model.

The aggregator reference is managed by Siena itself and you don’t have to deal with it neither declare it in your model.

Note Nevertheless, if you absolutely want to access your own aggregator, it’s possible. Go to the aggregated relation page.


The 2 important functions of Many<T> are:

  • SyncList<T> asList() : retrieves the elements as a SyncList which is a classic List with some (re)synchronization features
  • Query<T> asQuery() : retrieves the elements as a siena Query allowing to apply filters/orders/etc…

The 2 important functions of SyncList<T> are:

  • @SyncList sync()* : re-synchronizes only if the list was not already fetched from datastore (if the elements have changed since, it can’t know it so it won’t synchronize)
  • SyncList<E> forceSync() : forces full re-synchronization of the list with the datastore
  • boolean addAll(F ...c) : a facility function missing in traditional Java List
  • boolean addAll(int index, F ...c) : a facility function missing in traditional Java List
// creates dog
Dog pluto = new Dog("Pluto");
// infects dog with fleas
for(int i=0; i<10; i++){
	Flea flea = new Flea("pupuce"+i);
	pluto.fleas.asList().add(flea);
}
//saves dog + fleas in one single call
pluto.save();

Note You don’t have to insert the children objects as Siena inserts them at the same time as the parent (if the children were not inserted yet).

Note using asQuery() has no effect when inserting because it would try to query the datastore. Only asList() when creating the parent + children.


// gets the dog
Dog pluto = Dog.getByName("Pluto");
// here the fleas are not yet fetched from datastore
// ... later ...
// gets the little bloodsuckers
List<Flea> fleas = pluto.fleas.asList();

Note pluto.fleas is a Many<Flea> so you need to use asList() to fetch all the children


You can first insert a parent

Then, you create children along time and associate those children to the parent and insert them.

Finally, you can retrieve all children at once from the parent using asList()

// inserts dog
Dog pluto = new Dog("Pluto");
pluto.insert();

// infects dog with fleas
for(int i=0; i<10; i++){
	Flea flea = new Flea("pupuce"+i);
	// associates the flea to its dog
	flea.bloodsource = pluto;
	flea.insert();
}

// retrieves all the fleas from the dog
// gets the dog
Dog plutobis = Dog.getByName("Pluto");
// here the fleas are not yet fetched from datastore
// ... later ...
// gets the little bloodsuckers
List<Flea> fleas = plutobis.fleas.asList();

Note The children associate themselves to the parent but Many<T> retrieves them all from the parent afterwards.


// removes a few flea (5 & 8) for the poor dog 
plutobis.fleas.asList().remove(5);
plutobis.fleas.asList().remove(7);
plutobis.fleas.asList().add(new Flea("pupuce10"));
// updates the dog
plutobis.update();

// retrieve again the dog and verifies the fleas 5/8 have disappeared
Dog plutoter = Dog.getByName("Pluto");
assertEquals(9, plutoter.fleas.asList().size());

// retrieves the removed fleas and verifies there are not associated to the dog anymore
Flea flea5 = Flea.getByName("pupuce5");
assertNull(flea5.bloodsource);
Flea flea8 = Flea.getByName("pupuce8");
assertNull(flea8.bloodsource);
Flea flea10 = Flea.getByName("pupuce10");
assertEquals(pluto.id, flea8.bloodsource.id);

Note that the children are removed from the list but they are also de-associated from the parent BUT they are NOT deleted.


// gets a flea from the dog
Flea flea = pluto.fleas.asList().get(5);

flea.name = "pupuce5_UPD";

// if you update the dog, the flea is not updated
pluto.update();

// retrieves the same flea
Flea fleabis = pluto.fleas.asList().get(5);
assertFalse(flea.name.equals(fleabis.name));

// now updates the flea manually
flea.update();
		
// retrieves again the dog
pluto = Dog.getByName("Pluto");
// retrieves the same flea
fleabis = pluto.fleas.asList().get(5);
// verifies it was updated now
assertEquals(fleabis.name, flea.name);

Note Siena JUST MANAGES THE RELATION and do not update your objects if you modify them, it’s your responsability


// creates dog
Dog pluto = new Dog("Pluto");
// creates fleas
List<Flea> fleas = new ArrayList<Flea>();		
for(int i=0; i<10; i++){
	Flea flea = new Flea("pupuce"+i);
	fleas.add(flea);
}
//infects dog with fleas
//Just to show addAll(Object...)
pluto.fleas.asList().addAll(fleas);
//saves dog + fleas
pluto.save();

// fetches the list a first time
fleas = pluto.fleas.asList();

// gets flea n°5 independently of the dog and updates it 
Flea flea = Model.getByKey(Flea.class, fleas.get(5).id);		
flea.name = flea.name + "_UPD";
flea.update();

// synchronizes dog's fleas list (it doesn't re-fetch the list if already done) 
pluto.fleas.asList().sync();
// verifies the updated flea is not synchronized
Flea fleabis = pluto.fleas.asList().get(5);
// verifies it was updated
assertFalse(fleabis.name.equals(flea.name));

// forces synchronizing the list (it re-fetches the fleas) 
pluto.fleas.asList().forceSync();
// retrieves the same flea
fleabis = pluto.fleas.asList().get(5);
// verifies it was updated
assertEquals(fleabis.name, flea.name);

Note sync() doesn’t re-fetch the list as it was already fetched.

Note forceSync() forces the re-fetch of the list and all elements are synchronized with the datastore. Like that, you control exactly how you want to fetch data from the datastore after the first synchronization.

asQuery() is just an entrypoint to the Query world. Instead of getting all elements as asList() does, you can fetch only the elements you want by using query filtering/ordering/searching.

Here are a few samples:

// creates dog
Dog pluto = new Dog("Pluto");
// infects dog with fleas having several times the same name
Flea alpha1 = new Flea("alpha");
Flea alpha2 = new Flea("alpha");
Flea beta1 = new Flea("beta");
Flea beta2 = new Flea("beta");

//saves dog + fleas with addAll
pluto.fleas.asList().addAll(alpha1, alpha2, beta1, beta2);
pluto.save();

// retrieves again the dog
pluto = Dog.getByName("Pluto");

// fetches alpha fleas
List<Flea> alphas = pluto.fleas.asQuery().filter("name", "alpha").order("id").fetch();
assertEquals(alpha1.name, alphas.get(0).name);
assertEquals(alpha1.id, alphas.get(0).id);
assertEquals(alpha2.name, alphas.get(1).name);
assertEquals(alpha2.id, alphas.get(1).id);

// interates on beta fleas
Iterable<Flea> betas = pluto.fleas.asQuery().filter("name", "beta").order("id").iter();
int i=0;
for(Flea beta:betas){
	if(i==0) {
		assertEquals(beta1.name, beta.name);
		assertEquals(beta1.id, beta.id);
	}
	else if(i==1) {
		assertEquals(beta2.name, beta.name);
		assertEquals(beta2.id, beta.id);
	}
	i++;
}

Note addAll(Collection) is a convenient function to add several elements to a list without putting them in a Collection.

Note once you have a query with asQuery(), you can do what you want with it as it is a query ;)