To work with preliminary business data in one or multiple shared record instances, create a change proxy set with proxies of your shared records and introduce your changes to the proxies.
A proxy exists on a proxy level greater than 0: the "real" records exist on proxy level 0 and are not considered proxy objects. When you create a proxy from a record, the proxy exists on level 1; when you create a proxy from this proxy, it exists on proxy level 2, etc.
A proxy is created by a proxy set: a proxy set holds its proxies with their changes and holds the information on its proxy level. To apply the changes on the proxies held in a proxy set, merge the proxy set.
Proxies are useful especially in forms to allow to collect and validate all data before persisting it; refer to Using Proxies in Forms, and for usage examples to Creating a Model Popup and Creating a Public Popup.
A proxy is created by a proxy set and it is the proxy set that defines the proxy level of its proxies. Hence to create a proxy, you need to first create a proxy set:
createProxySet()
function:null
parameter.proxy()
method:To create a proxy of existing record instances:
proxy(T record*, Property... property)
or proxy(T record*, List<Property> properties)
proxies(Collection<T> records*, Property... properties)
or proxies(Collection<T> records*, List<Property> properties)
If the proxy of the record already exists in the proxy set, the method returns the proxy.
proxy(Type<T> recordType*)
proxyOfProperties(Record record*, Property... properties)
Example: Creating a proxy set on level 1 and creating a proxy:
Each proxy exists in a continuous chain of proxies across all previous proxy levels. If the proxy on a previous level does not exist, it is automatically added to the underlying proxy set on the lower level.
Consider the following case:
On runtime proxy of rec is created in set1 when it is created in set2. This allow successful merge of the proxy from set1 to set2 and to its rec when requested.
proxy(<RecordInstance>, <PropertiesOfRelatedRecords>, ..)
call when creating the proxy. Important: If you don't create a change proxy of a related record, the system will access the record instance on the underlying level when you navigate from the proxy to the related record. Let's take the following data model and code:
//shared record john is created and persisted: def Persona john := new Persona(address -> new Address()); //proxy set on proxy level 0 is created: def RecordProxySet rps := createProxySet(null); //proxy of the john record is created in the set: def Persona johnProxy := rps.proxy(john); //proxy john is changed: johnProxy.name := "John"; //NOT PROXIED address is changed: johnProxy.address.country := "USA";In this case, a record with an empty person and the address country "USA" is persisted.
Note that navigation across multiple relationships is not supported in the proxy()
call; hence for the example data model, this call is not valid:
To merge a proxy set into the underlying records or proxies, call merge(Boolean checkConflicts*)
, which returns the list of merged records.
Note: It is possible to merge a proxy directly to the underlying context without a RecordProxySets object; however, when you use a RecordProxySet and attempt to add a proxy object, such as, a proxy of a related record, the RecordProxySet checks if such a proxy already exists among its proxies thus keeping a clean tree of its proxied objects. It is strongly recommended to prefer RecordProxySets over direct merge.
If using optimistic locking on your records, consider using the merge call of with the Boolean parameter checkConflicts set to true so the merge checks for conflicts on the underlying records or proxies and handle the possible conflicts.
The merge(true)
call on a RecordProxySet fails if one of its proxies tries to merge its changes into a record with optimistic locking that has been changed by another transaction since the proxy was created.
For proxies of records with optimistic locking, consider either handling the possible optimistic-lock exception or adjust your data model so that the collisions cannot occur. For example, aggregate the data that might collide in a collection and create the resulting entity later.
Note that the merge(true)
call might succeed in this scenario
Catching OptimisticLockException
def RecordProxySet rps := createProxySet(null); rps.proxy(myRec); try rps.merge(true) catch "com.whitestein.lsps.common.OptimisticLockException" -> //log a message if the record has been changed: log("failed to merge changes", 100) end
Important: If you assign to a proxied property an object from a higher proxy level, on merge, the association is applied on the target object: the original relationship is removed.
To delete a proxy, call the deleteRecord() function on the proxy: this will delete the underlying record or proxy on merge.
You can check if a record is a proxy with the isProxy()
call.
You can check the proxy level of a proxy with a getProxyLevel(<Proxy>)
call, which returns an Integer. 0 designates the execution level with "real" context data.