I spoke a little too soon—my solution isn't sufficient, who'd have thunk it :).
Some of the columns with "fictional null values" are used in relationships. For example, I can have an invoice with customer_id "-1" to indicate that there's no related customer. As I'm done cleaning up the DB, these will eventually end up as actual foreign keys with nulls instead of that -1, so I'd like to model the relationship, hide the FK in the ObjEntity and use the "customer" relationship (I've actually modeled it like that already and it works fine for read operations, but of course everything explodes once I try to write and object with a null customer to the DB and "customer_id" is set to null). So, I kind of need to be doing this on the DbEntity level (if customer is null, write "-1" to the customer_id). Is this at all possible? Cheers, - hugi > On 4 Apr 2019, at 10:09, Hugi Thordarson <h...@karlmenn.is> wrote: > > Hi all, > I'm currently working on a legacy system. The DB has a lot of columns where > null should actually be allowed, but instead those fields are non-nullable > and get other values (empty string, "0", "-1" etc) written to them to > indicate the absence of a value. Yay. > > Unfortunately I can't just do the right thing; make the fields nullable and > start writing nulls, since that would make other (non Cayenne) parts of the > system explode, so I have to create a temporary workaround while I work > through each column and make it nullable and fix (or kill) the legacy apps. > > What I'd like to do is write my application logic as if the column was > nullable, setting nulls when I want to, but when the object goes to the DB, > "legacy nulls" are written to the DB instead (empty string or "-1" or > whatever). > > I have a working solution, I just wanted to check if you guys would have a > better or more performant solution or see anything potentially wrong with > what I'm doing. > > What I'm doing is creating an interface (HasFictionalNullValues) which can be > implemented by entity classes that require it. It defines a single method > that can be implemented like… > > public class SomeEntityClass extends _SomeEntityClass implements > HasFictionalNullValues { > > @Override > Map<Property,Object> fictionalNullValues) { > Map<Property,Object map = new HashMap<>(); > map.put( NAME, "" ); > map.put( AGE, -1 ); > return map; > } > > [...rest of class body...] > } > > …then I add a listener to my DataDomain to catch new and updated objects > > public class FictionalNullValuesListener { > > @PrePersist( { BaseDataObject.class } ) > @PreUpdate( { BaseDataObject.class } ) > public void onPrePersist( BaseDataObject object ) { > if( object instanceof HasFictionalNullValues ) { > > ((HasFictionalNullValues)object).fictionalNullValues().forEach( ( property, > replacementNullValue ) -> { > if( property.getFrom( object ) == null ) { > property.setIn( object, > replacementNullValue ); > } > } ); > } > } > } > > Any comments on this practice or something I'm missing? > > Cheers, > - hugi