Collection object of an entity fetches value after session close - spring-4

I have an entity having OneToMany relationship. After fetching the parent entity by below way:
User has many educational degrees.
#Transactional(propagation = Propagation.REQUIRED)
-----------at interface level--------------
public User getUser(int userid){
...
return user;
}
Now when trying to get the child collection(educational degree) , due to session is closed failed to lazily initialize a collection of role exception is occuring.
Please explain how collection object get loaded.

I found a solution, we should use #LazyCollection(LazyCollectionOption.FALSE) to load the collection of entities.

Related

EF manual attach list of entities into parent entity

class Student{
public string Name {get; set;}
public EntityCollection<Info> Infos {get; set;}
}
class Info{
public string Title {get; set;}
public Student Student {get; set;}
}
I have two entities like this. First I will query one student entity
var student = db.Students.FirstOrDefault(s => s.StudentId = 1);
Then I query Info list of this student in a separate query
var infos = from c in db.Info where c.StudentId = 1 and ....
If I loop though infos and add it manual into student.Infos, it will cause insert new row
foreach(info in infos){
student.Infos.Add(info);
}
How to attach list of info into student entity without insert new row into Info table when db.SaveChanges(). Like
student.Infos = infos
EF does the work for you behind the scenes when you use navigation properties. It's not just a data layer to load data singularly but rather it's set up with the relationships between the data and is capable of loading an entire object graph of related data either in one hit (eager loaded) or on-demand (lazy loaded)
Firstly: you can update your Info collections to ICollection<Info> or List<Info>. I opt for List<Info> because I commonly use .AddRange(). Also, mark it as virtual to enable EF proxies and lazy loading.
From there, to access the Infos on a Student you can just use:
var student = db.Students.Include(s => s.Infos).SingleOrDefault(s => s.StudentId = 1);
This will eager-load the Infos for the selected student. No need to load them separately.
If you leave off the .Include(..) then you can still access the Infos (provided the DbContext is still in scope) though this will trigger additional SQL calls to load the Infos. (Lazy loaded)
When loading data to send outside of the scope of the DbContext, such as returned from an API call, or sent to a view, it's recommended to compose a DTO or ViewModel with just the fields that you need from the various entities, then perform a .Select() to populate them, and return the DTOs not the entities. This avoids problems with lazy loading calls after a DbContext has been disposed and unexpected performance issues if lazy loading is triggered due to serialization or the like.

Spring Data Neo4j 4: Bug when updating property to null?

I use Spring Data Neo4j 4 GraphRepository to save and retrieve data. Using GraphRepository save() and findAll() methods.
When I update an existing entity property to null, it seems that changes are not reflected in the returned data.
If I update the property to any other non-null value, the changes are reflected correctly.
I can see that the null property update is performed on the DB server. But the findAll() method doesn't reflect the change and keeps the old value.
Is this a known bug? Any workaround? Or is it some kind of caching problem?
UPDATE
After trying to understand what happens, I found that this problem will occur when you have two different Java objects for the same entity. The null property will never be updated (but other properties with non-null values will).
Example code:
#Autowired
MovieRepository repository;
public void test() {
repository.deleteAll();
Movie movie1 = new Movie();
movie1.setName("Pulp Fiction");
movie1.setDirector("Quentin Tarantino");
movie1 = repository.save(movie1);
System.out.println("Movie1: " + movie1);
Movie movie2 = new Movie();
movie2.setId(movie1.getId());
movie2.setName(movie1.getName());
movie2.setDirector(null); // implicit...
movie2 = repository.save(movie2);
System.out.println("Movie2: " + movie2);
Movie movie3 = repository.findOne(movie1.getId());
System.out.println("Movie3: " + movie3);
}
Real life case: when using SDN with a Spring MVC form, it looks like entities are created from Model attributes. When a value is set to null in a form, the update is performed correctly in Neo4j, but the values are not returned correctly when using any find...() methods. Therefore it leads to stale data.
Side note: this problem happens when the Neo4J session scope is per "session" and doesn't happen when the session scope is per "request".
#Bean
#Override
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
If you are using HttpSession-scoped persistence in SDN, you should ensure the the objects bound to your Controller via #ModelAttribute have the same scope as the persistence layer. Use the #SessionAttribute annotation on the Controller to achieve this.
If you use HttpRequest-scoped objects in your Controller and HttpSession-scoped persistence, you will get different objects representing the same graph entity at the web layer, and this will confuse the persistence mechanism.
Should not be a problem at all. I just tried
#Test
public void shouldPersistNulls() {
TempMovie movie = new TempMovie( "Pulp Fiction" );
tempMovieRepository.save( movie );
assertSameGraph( getDatabase(), "CREATE (m:Movie {name:'Pulp Fiction'})");
TempMovie loadedMovie = tempMovieRepository.findAll().iterator().next();
loadedMovie.setName(null);
tempMovieRepository.save(loadedMovie);
assertSameGraph( getDatabase(), "CREATE (m:Movie)");
TempMovie loadedAgainMovie = tempMovieRepository.findAll().iterator().next();
assertNull(loadedAgainMovie.getName());
}
and it passed.
Update based on edited question
The property representing the #GraphId must never be set manually i.e. via your code. You should load the entity by id when you require to update it. This ensures that the entity is known to the mapping context of the OGM and is managed correctly.

Symfony2 - Check if a Doctrine Entity Association has been Initialized/Loaded without Triggering Lazyload

I have an entity called foo which has an OneToMany association with an entity called bar that is accessible as $foo->getBar() (an ArrayCollection). Normally calling $foo->getBar() would trigger a Lazy Loading of associated bar entities (if they weren't joined originally).
How can I check if bar has been loaded without triggering a Lazy Load? I don't need the associated entities, if they weren't loaded originally, and I don't want them to load, I just want to know IF they were loaded.
Example
In the fooRepository I have a method called getFooWithBar() and that has a join which loads all the bars as an ArrayCollection and returns foo with all the associated bar entities. But if I just call a simpler method like getFooById() with a simple query, the bar entities were not loaded with a join, so they are not contained in $foo.
So in another controller I have $foo, and I want to check if getBar() has associated entities loaded yet, but I do not want to trigger the Lazy Loading. If it doesn't have associated entities, I don't want them. I just need to know IF they have been loaded.
NOTE: I also do not want to turn off Lazy Loading on the entity association for all instances.
Method that Doesn't Work for Inverse side of OneToMany
I put this magic getter method in my entity:
public function __get($property) {
return isset($this->$property) ? $this->$property : null;
}
Which theoretically lets me check if the property is set (or if it's still the default private declaration). And this works when my entity is the owning side. But if it's the inverse side, $this->property is never set. Doctrine does some fancy stuff so that when you do getProperty() it's looking at the data somewhere else. I figured this out because this function works when it's the owning side (it returns a proxy of the associated entity), but it returns null when the associated entity is owned by the other entity.
After years of testing our code (responding to Doctrine changes) the following is the best solution we could come up with to check if an association has been loaded, WITHOUT trigger LazyLoad. None of this stuff is documented in Doctrine (unfortunately), so you have to look at the source code and/or play with the code.
The Solution
In the end there are many different types of different associations that could be loaded from *ToMany (PersistentCollection) or *ToOne associations (Proxy or direct entity). This means we need to create a method that checks for all the possibilities (that we are currently aware of in our app). We created a trait that we add to all our entities, so we can call $entity->isLoaded($propertyName) to check if it's loaded.
public function isLoaded($property)
{
// *ToMany Association are PersistentCollection and will have the isInitialized property as true if it's loaded
if ($this->{$property} instanceof PersistentCollection) {
return $this->{$property}->isInitialized();
}
// *ToOne Associations are (sometimes) Proxy and will be marked as __isInitialized() when they are loaded
if ($this->{$property} instanceof Proxy) {
return $this->{$property}->__isInitialized();
}
// NOTE: Doctrine Associations will not be ArrayCollections. And they don't implement isInitalized so we really
// can tell with certainty whether it's initialized or loaded. But if you join entities manually and want to check
// you will need to set an internal mapper that records when you've loaded them. You could return true if count > 0
if ($this->{$property} instanceof ArrayCollection) {
// NOTE: __isLoaded[$property] is an internal property we record on the Setter of special properties we know are ArrayCollections
return (!empty($this->__isLoaded[$property]) || $this->{$property}->count() > 0);
}
// NOTE: there are never any Collections that aren't ArrayCollection or PersistentCollection (and it does no good to check because they won't have isInitialized() on them anyway
// If it's an object after the checks above, we know it's not NULL and thus it is "probably" loaded because we know it's not a Proxy, PersistentCollection or ArrayCollection
if (is_object($this->{$property})) {
return true;
}
// If it's not null, return true, otherwise false. A null regular property could return false, but it's not an Entity or Collection so indeed it is not loaded.
return !is_null($this->{$property});
}
When you load your foo object, bar will be an instance of Doctrine\ORM\PersistentCollection. You can call the isInitialized() method on this collection to find out if has been initialized.
For Associations that are an ArrayCollection:
$initialized = $foo->getBar()->isInitialized();
If you have newest version of Doctrine, you can try extra lazy load on column.
More Extra lazy associations

EF 5.0 Trouble updating entity which is already tracked

I'll preface this question with the following: I know there are a million posts on the internet about the old "An object with the same key already exists in the ObjectStateManager" issue. My scenario is a bit more complicated, I think.
I have a UnitOfWork class which creates a DbContext and passes it to any repository which is called. The pattern I'm using closely follows the Unit of Work tutorial on the ASP.NET site. Unlike the tutorial, my repositories take in Business entities, map them to data entities, and perform some CRUD action. My Business logic only works with Business entities. Here is what I'm trying to do in a sample Business Manager class:
_unitOfWork.Repository.Add(entity);
_unitOfWork.Save(); // context.SaveChanges() under the hood
...Perform some operations on the model...
_unitOfWork.Repository.Update(entity);
_unitOfWork.Save();
Here is a sample Update method from the repository:
public virtual void Update(entity)
{
var dataEntity = // map from business entity to data;
_context.Entry(dataEntity).State = EntityState.Modified;
}
It obviously fails on the last line. Here is where my confusion sets in:
The entity's State is Detached
When I attempt to change the State to Modified or Unchanged, it gives me the ObjectStateManager exception above.
When I attempt to detach the entity from the context (((IObjectContextAdapter)_context).ObjectContext.Detach(entity);) I get an exception about how the entity is not attached to the context, therefore, it cannot detach it. Very confusing (something fundamental I'm missing, for sure).
Many other posts suggest I make a database call, update that entity in the repository, then _unitOfWork.Save(). I don't like this approach. I shouldn't need to make an unnecessary network call to update an entity.
The Update method in the repository needs to handle two scenarios: 1) updating an entity which is not currently tracked by the context, and 2) updating an entity which IS currently tracked by the context. The second piece is what I'm struggling with.
Any help or insight is appreciated.
Thanks!
This means that there already is an object attached to the context with the same key as the new dataEntity. The existing object and the new entity both represent the same entry in the database but they are two different objects.
This may indicate that the lifespan of your _context is too long, but that's hard to judge from your code. It is certain though that the context was previously used to fetch an entity from the database that is subsequently duplicated by var dataEntity = ....
You may have to shorten the lifespan of the context, I can't tell. If you think it's OK you may want to use the Local collection to check whether the entity is already there. That will save the database round trip that Find may still make.
I found a hybrid solution which appears to work:
public virtual void Update(TB entity)
{
var dataEntity = Mapper.Map<TB, TD>(entity);
var pkey = _dbSet.Create().GetType().GetProperty("Id").GetValue(dataEntity);
var entry = _context.Entry(dataEntity);
if (entry.State == EntityState.Detached)
{
var attachedEntity = _dbSet.Find(pkey);
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(dataEntity);
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
entry.State = EntityState.Modified;
}
}

ASP.NET custom MembershipProvider and shared DbContexts

I have created a custom MembershipProvider in an MVC web appliocation. My GetUser function returns an instance of my own custom Employee class which inherits from the standard MembershipUser. This allows me to supply additional details for the each user such as various employee details.
public override MembershipUser GetUser(string username, bool userIsOnline)
{
return new ModelRepository().GetModels<Employee>().Where(e => e.UserName == username).FirstOrDefault();
}
The problem I'm having, the membership provider spawns a new instance of my repository class (which creates a new DbContext) to retrieve the Employee object. This Employee object is then passed to whatever request/controller action called the Membership api.
Employee currentUser = (Employee)Membership.GetUser();
That calling request will often want to create a new object in memory, lets say a new SicknessRecord and assign the user retrieved earlier to that record and then save it to the DB with its own model repository. You can probably see where this is going, the framework complains that I'm trying to save an object (the user) with a context that it wasn't initially retrieved with.
My current, rather hackish solution is to just use the ID of the user retrieved from the Membership.GetUser and go and re-retrieve the Employee object from my current model repository.
newSickness.Employee = this.modelRepository.GetModelById<Employee>(this.me.Id.Value);
I've tried detaching the Employee object but then it loses its lazy loaded properties and I have to remember to try and attach it again to my current repository/context.
I've also read it is good to have your custom membership provider share the same context that the current request would be using. Any ideas how to achieve this, how do I ensure the membership provider uses the same context as the one spawned when a user executes a controller action?
You can have one separate DbContext instance for each controller.
public class SomeController : Controller
{
private DbContext context = new DbContext();
private CustomMembershipProvider membershipProvider = new CutomMembershipProvider(this.context);
... Actions ....
}
As far as I know, one context per controller is good practice.
By the way, if you have several repositories try to use UnitOfWork with Repository pattern.

Resources