• How to copy or 'save as' a record using ReStore

    From Scott McWilliams@21:1/5 to All on Wed Sep 13 13:05:32 2023
    What is the recommended approach to to copy or 'save as' a record using ReStore? Using #copy or #deepCopy just seems to update the original.

    I have tried creating a new, 'empty' object of a class and populating just the fields of interest, which seems to work sometimes, but other times results in either an 'index of bounds' error or fails trying to (re-) insert a duplicate key value in a
    related table.

    Thanks in advance for any and all ideas.

    Regards,
    Scott

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to Scott McWilliams on Fri Sep 15 09:14:24 2023
    Hi Scott,

    When copying objects in Smalltalk you may also need to copy some component objects to ensure the copy has a sensible structure. This is particularly important when copying objects persisted with ReStore (in some cases ReStore will complain about invalid
    structures) however the principle is the same as for non-persistent objects.

    As an example I'll use the classes from the "SSW ReStore Examples" package (see the package comment for more details). Starting with an empty database let's first create a Customer with an Address:

    Customer new
    firstName: 'John';
    surname: 'Smith';
    address: (Address new line1: '123 Oxford Street'; yourself);
    store.

    You should now have a database containing one Customer and one Address:

    Customer storedInstances size. "1"
    Address storedInstances size. "1"

    Now let's try to copy the customer and persist the copy:

    customer := Customer storedInstances first.
    copy := customer copy.
    copy firstName: 'James'; store.

    At this point you should receive the error "attempt to assign collection to > 1 persistent object". This is because the default implementation of copy will just return a shallowCopy, so the copy has the exact same 'orders' collection as the original.
    Sharing persistent collections isn't valid in ReStore so the copy needs its own collection - we do this by adding an implementation of postCopy to Customer:

    postCopy

    super postCopy.
    self orders: OrderedCollection new

    We should now be able to create and persist a new copy of the customer:

    copy := customer copy.
    copy firstName: 'James'; store.

    However note that although we've copied the customer the address has not been copied:

    Customer storedInstances size. "2"
    Address storedInstances size. "1"

    The single persistent Address is shared by both customers (it's valid to share persistent non-collection objects in ReStore). It may be that this would be a valid situation in some databases, however for a database of customers lets assume we want each
    customer to have its own independent address, so we should also copy the address as part of the customer's postCopy method:

    postCopy

    super postCopy.
    self orders: OrderedCollection new.
    self address: self address copy

    If we now create and persist an additional copy we will get both a new customer and a new address:

    copy := customer copy.
    copy firstName: 'Jack'; store.

    Customer storedInstances size. "3"
    Address storedInstances size. "2"

    Just to reiterate, the important thing is to implement postCopy methods in your persistent classes so that the resulting copies represent a valid structure according to the requirements of your data model.

    Hope this helps,

    John Aspinall


    On Wednesday, September 13, 2023 at 9:05:33 PM UTC+1, Scott McWilliams wrote:
    What is the recommended approach to to copy or 'save as' a record using ReStore? Using #copy or #deepCopy just seems to update the original.

    I have tried creating a new, 'empty' object of a class and populating just the fields of interest, which seems to work sometimes, but other times results in either an 'index of bounds' error or fails trying to (re-) insert a duplicate key value in a
    related table.

    Thanks in advance for any and all ideas.

    Regards,
    Scott

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott McWilliams@21:1/5 to [email protected] on Wed Sep 20 06:30:55 2023
    On Friday, September 15, 2023 at 11:14:25 AM UTC-5, [email protected] wrote:
    Hi Scott,

    When copying objects in Smalltalk you may also need to copy some component objects to ensure the copy has a sensible structure. This is particularly important when copying objects persisted with ReStore (in some cases ReStore will complain about
    invalid structures) however the principle is the same as for non-persistent objects.

    As an example I'll use the classes from the "SSW ReStore Examples" package (see the package comment for more details). Starting with an empty database let's first create a Customer with an Address:

    Customer new
    firstName: 'John';
    surname: 'Smith';
    address: (Address new line1: '123 Oxford Street'; yourself);
    store.

    You should now have a database containing one Customer and one Address:

    Customer storedInstances size. "1"
    Address storedInstances size. "1"

    Now let's try to copy the customer and persist the copy:

    customer := Customer storedInstances first.
    copy := customer copy.
    copy firstName: 'James'; store.

    At this point you should receive the error "attempt to assign collection to > 1 persistent object". This is because the default implementation of copy will just return a shallowCopy, so the copy has the exact same 'orders' collection as the original.
    Sharing persistent collections isn't valid in ReStore so the copy needs its own collection - we do this by adding an implementation of postCopy to Customer:

    postCopy

    super postCopy.
    self orders: OrderedCollection new

    We should now be able to create and persist a new copy of the customer:

    copy := customer copy.
    copy firstName: 'James'; store.

    However note that although we've copied the customer the address has not been copied:

    Customer storedInstances size. "2"
    Address storedInstances size. "1"

    The single persistent Address is shared by both customers (it's valid to share persistent non-collection objects in ReStore). It may be that this would be a valid situation in some databases, however for a database of customers lets assume we want each
    customer to have its own independent address, so we should also copy the address as part of the customer's postCopy method:

    postCopy

    super postCopy.
    self orders: OrderedCollection new.
    self address: self address copy

    If we now create and persist an additional copy we will get both a new customer and a new address:

    copy := customer copy.
    copy firstName: 'Jack'; store.

    Customer storedInstances size. "3"
    Address storedInstances size. "2"

    Just to reiterate, the important thing is to implement postCopy methods in your persistent classes so that the resulting copies represent a valid structure according to the requirements of your data model.

    Hope this helps,

    John Aspinall
    On Wednesday, September 13, 2023 at 9:05:33 PM UTC+1, Scott McWilliams wrote:
    What is the recommended approach to to copy or 'save as' a record using ReStore? Using #copy or #deepCopy just seems to update the original.

    I have tried creating a new, 'empty' object of a class and populating just the fields of interest, which seems to work sometimes, but other times results in either an 'index of bounds' error or fails trying to (re-) insert a duplicate key value in a
    related table.

    Thanks in advance for any and all ideas.

    Regards,
    Scott

    Thank you John!

    Regards,
    Scott

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)