1

I want to store Person objects in DB4O. The Person Location field can be changed over time. So I retrieve a person from the DB and call a method to set the location field to a new Location object. (I want the Location objects to be immutable i.e. DDD Value Objects).

This works, however the previously assigned Location objects remain the database. How can I configure DB4O to remove these orphaned Location objects? Or do I need some custom process to garbage collect?

Simplified classes for this example:

class Person {
    Location location;
    public void Move(Location newLocation) { 
        location = newLocation;
    }
}

class Location {
    public Location(string city) { 
        this.City = city;
        //etc
    }
    public readonly string City;
    /// more fields...
}

EDIT: Some more information - Person is meant to be an DDD aggregate root. So there are no external references to a person's internal state. If Person updates its location, then the old location should cease to exist.

Andrew Davey
  • 5,441
  • 3
  • 43
  • 57

5 Answers5

1

I think there's no perfect solution available. But with some work you can nearly achieve this behavior. A similar topic is already discussed here.

The first step would be to activate cascade-deletion on the location-field. So when a Person is deleted the location is also deleted.

configuration.common().objectClass(Person.class).objectField("location").cascadeOnDelete(true);

Now we need to handle the changing location case. The idea is this:

  1. Register on the activate-event. There you 'remember' which object was embedded
  2. Register on the update-event. There you check if it's still the same embedded object. If not, you delete the old on.
  3. Very important: Never ever 'share' the embedded object, otherwise it will be deleted for everyone. A simple solution is to create a copy of the Location-object whenever it's assigned.

There is Java-Demo which implements this behavior.

Well, this is only the concept, it's a long way to an acceptable solution:

  1. Utilize Attributes or other configuration to specify which objects are such
  2. Build a robust implementation of the event-handlers etc.
  3. A solid solution to ensure that a 'shared' location is not deleted
Gamlor
  • 12,978
  • 7
  • 43
  • 70
  • This is an excellent answer. I have been investigating using events to do exactly this. I'm glad we agree that it's a viable approach. – Andrew Davey Mar 08 '10 at 11:30
0

According to db4o 8.0 API reference of cascadeOnDelete(boolean), the old object should be deleted automatically. Here is a copy of the doc, check the given example.

sets cascaded delete behaviour. 

Setting cascadeOnDelete to true will result in the deletion of all member objects of instances of this class, if they are passed to ObjectContainer.delete(Object). 

Caution !
This setting will also trigger deletion of old member objects, on calls to ObjectContainer.store(Object).

An example of the behaviour:

ObjectContainer con;
Bar bar1 = new Bar();
Bar bar2 = new Bar();
foo.bar = bar1;
con.store(foo); // bar1 is stored as a member of foo
foo.bar = bar2;
con.store(foo); // bar2 is stored as a member of foo 

The last statement will also delete bar1 from the ObjectContainer, no matter how many other stored objects hold references to bar1. 

The default setting is false.

In client-server environment this setting should be used on both client and server. 

This setting can be applied to an open object container. 

Parameters:
flag - whether deletes are to be cascaded to member objects.
See Also:
ObjectField.cascadeOnDelete(boolean), ObjectContainer.delete(Object), Using callbacks

However it doesn't work as printed, weird.

szcoder
  • 1,396
  • 11
  • 9
0

How about using db4o's delete() first on the old location and then storing the new one?

Best!

German

Curtis
  • 101,612
  • 66
  • 270
  • 352
German
  • 1
  • I've made the code sample more representative of my situation. The location field is private. I want the persistence layer to automatically delete the old location object. Otherwise I have to start digging down into every object just clean things up. – Andrew Davey Mar 04 '10 at 17:17
0

Have you thought about making it a value type?

  • I'd rather not have to start using structs everywhere below the top-level object. – Andrew Davey Mar 05 '10 at 12:41
  • Yeah, that could be a mess... they should add the ability to treat a reference object as a value type from the database perspective. –  Mar 05 '10 at 18:00
0

This really looks like a transaction to me.

Like German said, you will have to delete the old, store and assign the new one and make sure these steps can be committed in one go.

In a RDBMS, you'd have to come up with a transaction for this, too. However, many RDBMS systems support you here with triggers and events. Note that db4o also offers certain callbacks.

I'm currently working on a ref-count abstraction for cases like this, but it is very tricky to handle generically. On the other hand, you could write a specific Update method that simplifies the transaction for you and compares old and new objects' references. If they don't match and you can be sure that nobody else references that type of address object, you can delete it.

Also note that were you using a language without garbage collection, you'd also have to keep track of this manually and delete the old object.

That 'aggregate root' concept seems very vague to me - after all, it depends on the perspective, but that is another issue.

mnemosyn
  • 45,391
  • 6
  • 76
  • 82
  • I'm investigating traversing the object reference graph from a root object upon activation, storing this and then on update I can compare against the updated object graph. Anything missing can be deleted. – Andrew Davey Mar 07 '10 at 13:35
  • That means you are assuming your location objects are never ever referenced by anything else? I still don't feel that deletion of objects should take place in some hidden, automatic manner but that seems to be a matter of taste mostly -- after all, garbage collection is a cool feature, too. Personally, I'm not ready for such a feature on persisted data though. – mnemosyn Mar 07 '10 at 14:15