Grails MongoDB Embedded Domain Objects Saving - mongodb

When attempting to save a Grails Domain object that is backed by Mongo all of my embedded objects are having save() called on them as well. In some instances this is causing some tremendous affect on performance.
Some example domain objects are as follows.
class Bird {
Object Id
String name
}
class Nest {
static embedded = ['birds']
String name
Set<Bird> birds
}
class Tree {
static embedded = ['nests']
Object Id
String name
}
class TreeState {
static embedded = ['tree']
Object Id
Set<Nest> nests
Tree tree
Date dateCreated
}
Now let's say I wanted to save the state of a tree at a given time.
def tree = Tree.findByName("Sleepwood")
def nestA = new Nest()
nestA.birds = new LinkedHashSet()
nestA.name = "Sleepwood-A"
def nestB = new Nest()
nestB.birds = new LinkedHashSet()
nestB.name = "Sleepwood-B"
def blueJay = Bird.findByName('Blue Jay')
def cardinal = Bird.findByName('Cardinal')
def oriole = Bird.findByName('Oriole')
def robin = Bird.findByName('Robin')
def treeState = new TreeState()
treeState.nests = new LinkedHashSet()
treeState.tree = tree
nestA.add(blueJay)
nestA.add(cardinal)
nestA.add(oriole)
nestB.add(oriole)
nestB.add(blueJay)
nestB.add(robin)
treeState.nests.add(nestA)
treeState.nests.add(nestB)
treeState.safe(failOnError: true)
This sort of action seems to cause the embedded entities, Bird and Tree to be saved as values but also seem to be saved as well to update their associated domain models.
Is there a way I can prohibit these references from being updated and just save the embedded fields in the TreeState object?

Related

Avoid lazyloader attribute

I´ve been looking for how avoid return a list without the attribute lazyLoader, I want to continue using the lazyLoader but I don´t want return the attribute when I return the whole list of my entity from my controller
I´m working with .NET core.
[
{
"lazyLoader": {},
"id": "id1"
"name": "name"
},
{
"lazyLoader": {},
"id": "id2",
"name": "name2"
}
]
You can do a select of you collection only retrieving the rest of the data.
That way your objects will not have the Navigation property at all.
db.YourCollection.Where(your condition)Select(x => new { id = x.id , name = x.name } );
In Entity Framework, if you have an object where one or more of its properties use lazy loading, check its runtime type name using GetType().Name. For an object of a Car class, for example, you will notice that the runtime type is actually something called CarProxy, which is a temporary in-memory type created by Entity Framework using reflection. This "fake" proxy class's base type is Car, and has all the original Car properties, but includes an extra one called LazyLoader for properties that may need it.
If you do further checking on this "fake" CarProxy type, you will also see that Assembly.IsDynamic = true, which is indicative that the class was created dynamically using reflection (see documentation):
var TheCar = DBContext.Cars.Find(1);
Console.WriteLine(TheCar.GetType().Assembly.IsDynamic.ToString()); //will echo "true"
Luckily, Newtonsoft.Json has an override on the JsonConvert.SerializeObject() method that allows us to provide a base type, so that the resulting JSON doesn't contain properties that don't exist in that type. So, to eliminate the LazyLoader property, just provide the object's BaseType as the type parameter:
var TheCar = DBContext.Cars.Find(1);
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType);
To make sure you don't get any circular reference loops when serializing (a very high probability when using lazy loading), call the serializer with the following setting:
var TheCar = DBContext.Cars.Find(1);
var Settings = new Newtonsoft.Json.JsonSerializerSettings
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType, Settings);
Note: This may only work on the first level deep when the serializer travels through the object. If there are yet more lazy-loading child properties of the object you provide to the serializer, the "LazyLoader" property may appear again. I haven't tested it so I can't say for sure.
I know this is old, but add
public boolean ShouldSerializeLazyLoader() { return false; }
to all the classes down the tree of the ones you want to serialize, and you will get a lazyloader free JSON.
Ref.: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
The checked answer for this question is just working for the root object, if we have many nested lazyloaded objects, this solution will not work.
Although the answer of #Marcello-Barbiani is correct but it is not a good way to add this function to all entities we have.
The best way is create a new ContractResolver derived from DefaultContractResolver and check if property is Lazyloader then skip it as below:
public class NonLazyloaderContractResolver : DefaultContractResolver
{
public new static readonly NonLazyloaderContractResolver Instance = new NonLazyloaderContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyName == "LazyLoader")
{
property.ShouldSerialize = i => false;
}
return property;
}
}
after that adding above class pass it through JsonSerializerSettings while serializing the object:
var json = JsonConvert.SerializeObject(newProduct, new JsonSerializerSettings() {
ContractResolver = new NonLazyloaderContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore });
and finally if you are using asp.net core or asp.net core webapi add this contract as default contractresolver in startup.cs file:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new NonLazyloaderContractResolver();
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});

JPA and "anonymous" classes in scala

I'm a bit stuck and don't understand what's going on.
This one doesn't work
#Entity
#DynamicInsert
#DynamicUpdate
#SelectBeforeUpdate
#Table
class Entity {
#Column(nullable = false)
var owner: String = _
}
val myEntity = new Entity() {
owner = "some owner 1"
}
session.persist(myEntity)
Hibernate throws exception:
java.lang.IllegalArgumentException: Unknown entity:persistence.dao.EntityDaoTest$$anonfun$13$$anonfun$14$$anon$5
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:777)
This one works:
val myEntity = new Entity()
entity.owner = "some owner 1"
session.persist(myEntity)
Why? Why does hibernate don't recognize my Entity instance?
UPD:
#Sheinbergon, thanks, it's clear. I completely forgot that annotations are lost. Is there any possibility to set entity fields with some shortcut?
writing
val myEntity = new MyEntity()
myEntity.owner = "some owner"
myEntity.someOtherProperty = "value"
is super boring
One more question
This one works:
val parent = new Parent
parent.owner = "Our parent"
parent.addChild(new Child() {
name = "First parent's child"
addGrandChild(new GrandChild() {
name = "Grand child name"
addGrandGrandChild(new GrandGrandChild() {
name = "Grand Grand child name"
address = new Address() {
id = 1L
}
})
})
})
Why? Child, GrandChild, GrandGrandChild also created anonymously.
addChild, addGrandChild, addGrandGrandChild are just list mutators.
def addChild(child: Child): Unit = {
if (children == null) {
children = new util.ArrayList[Child]()
}
if (Option(child.parent).isEmpty) {
child.parent = this
}
children.add(child)
}
What you are doing here is instantiating a class anonymously in Scala , and well... that creates an anonymous implementation of your class Entity ( like instantiating an interface anonymously in Java).
you can see it by printing the class name - println(myEntity.getClass) in both cases
Annotations applied to the original class do not apply to the anonymous one (reflection can still find them in the super class, but that's up to the code scanning them) and I guess that's why you're getting the various JPA exceptions
In response to your added sub-questions
Regarding a shortcut - why don't you use companion objects for factories or turn this class into a case class (with defaults), allowing for nicer, more flexible initialization.
Regarding the second object graph(and assuming eachof your classes are annotated) - again it depends on how the reflective code treats the objects it scans. it's possible ( and more likely, given that it won't scan each member of the collection for annotations ) it takes annotation definitions from the erased type ( possible to get it's FQDN class name as ParameterizedType in Java's reflection API) of the collection and not from the actual members of the collection and that's why it works.
I'm not really sure what it does about field definitions though (they are only present in the "super" class), but there's no "magic" here, just plain old reflection scans.

Can I use CLIMutable records with EntityFramework in an update scenario?

I am trying to use F# record types with the CLIMutable attribute as entities with Entity Framework. Selecting entities, and adding new entities works fine, but there is a problem with updating entities. Apparently I cannot use the assignment operator (<-) to assign a value to the field of a mutable record type.
Is there any way to make an assignment to the field of an existing record?
[<CLIMutable>]
[<Table("UserProfile")>]
type UserProfile = {
[<Key>]
Id : int
Username : string }
type Context() =
inherit DbContext(connectionString)
[<DefaultValue>]
val mutable private _users : DbSet<UserProfile>
member this.Users
with get() = this._users and set(v) = this._users <- v
let updateUsername id username =
use context = new Context()
let user = context.Users.Find(id : int)
user.Username <- username //This line does not compile
context.SaveChanges()
From the documentation:
type Car = {
Make : string
Model : string
mutable Odometer : int
}
let myCar = { Make = "Fabrikam"; Model = "Coupe"; Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
The CLIMutable attribute is there so that frameworks etc can populate records. If you need to modify the record yourself, you simply need to inform the F# compiler that you want to be able to update that field.

Scala: Avoid mutable instance variables

I am trying to avoid mutable instance variables in class but I am not able to figure out how in the following case while trying to create a User in my Play application:
The user information like the membershipList can change while the user is logged in. When such a thing changes, the application notifies the user using Play's Ok.feed(...) as Server Sent Event (SSE).
EDIT: #drstevens pointed out that the Unicast feed must be moved out as User was shown as Case class. Editing to make Usernot a case class.
class User(id: Long, firstName: String, lastName: String,
membershipList: List[Group], #volatile private var signedIn = true) {
private var infoSentOnce = false;
private val unicastFeed: Option[Enumerator[JsValue]] = if(signedIn) Concurrent.unicast[JsValue](onStart, onComplete, onError) else None
private def onStart(channel: Channel[JsValue]) {
if(signedIn) {
if(!infoSentOnce) {
channel.push(json)
channel.end
infoSentOnce = true;
}
} else {
channel.eofAndEnd
}
}
private def onComplete = ....
private def onError(message: String, iteratee: Iteratee[JsValue]) = .....
def json = {
.....
}
def signedOut = signedIn = false
def feed = unicastFeed
}
Action in the controller will be:
class MyController extends Controller with MyAuth {
def userFeed = Authenticated { request =>
request.user.feed.fold(Ok(request.user.json)) { feed =>
Ok.feed(feed
>& Concurrent.buffer(10)
>& new EventSource()).as("text/event-stream"))
}
}
}
signedIn & infoSentOnce are the two mutable variables that I would like to change it to be immutable. infoSentOnce being mutable may also relate to me not understanding Play's enumerator properly. Whenever the user information changes, I create a new User object but I copy over the Unicast enumerator from the previous instance of the same user. Json is to be pushed once into the user's Unicast channel when a new User instance gets created.
Maybe having infoSentOnce as var is okay here as it cannot be modified by any outside interactions?
Since your User class is handling events itself, it is going to have to maintain state, and that means being mutable.
Immutable classes are most useful when things are done to them by outside forces, like if MyController is signing in the user, and can make a user = user.copy(signedOn = true).
If the class itself is managing the state, well, then, you need mutability.
You can sort of cheat here to get all vals by replacing your boolean variables with AtomicBooleans, but that just moves the mutability one level away.

Non-static inner object in class

I am trying to create a builder for my form fields and I come with something like:
class Select(name:String) {
object cell extends RequestVar("all")
/* Do some stuff with name and cell */
}
val fruitsField = new Select("fruits")
val drinksField = new Select("drinks")
Now I can do:
fruitsField.cell.set("tomato")
drinksField.cell.get // returns "tomato"
When using those fields I realized that cell was shared by fruits and drinks. I know that it is the normal behavior but is there a way to have this inner object cell to be non-static?
EDIT
I tried the following but it has the same behavior:
class Select(name:String) {
class ReqVar extends RequestVar("all")
val cell = new ReqVar
/* Do some stuff with name and cell */
}
Attention, the Lift documentation (2.4-M4) states about RequestVars (and SessionVars):
If you find it necessary to create a RequestVar subclass of which there may be more than one instance, it is necessary to override the nameSalt() method to return a unique salt value for each instance to prevent name collisions.
So you will have to do something like this:
class Select(name:String) {
val cell = new RequestVar("all") {
override def __nameSalt = "select_cell_" + name
}
/* Do some stuff with name and cell */
}
Of course this will only work if there exists only one instance of Select with a given name per Request.
EDIT
Based on what you just posted
class Select(name:String) {
class ReqVar extends RequestVar("all") {
override def __nameSalt = "select_reqvar_cell_" + name
}
val cell = new ReqVar
/* Do some stuff with name and cell */
}
Is anything wrong with
val cell = new RequestVar("all")
?