OpenJPA - lazy fetching does not work - jpa

I have a specific problem with an unit test using embedded OpenEJB container. I have a bi-directional relation between two classes. In one direction the relation works properly, but in the opposite direction the relation works only in EAGER-mode. In LAZY-mode the field section stays null. The code snipped follows:
#Entity
#Table(name="tracks")
class TrackEntity implements Track {
#Id
private int trackNumber;
#OneToMany(mappedBy = "track")
private HashSet<SectionEntity> sections;
public TrackEntity() {
sections = new HashSet<SectionEntity>();
}
#Override
public Collection<HistoricalEvent> getEvents() {
if (sections == null)
throw new CommonError("number=" + trackNumber, AppErrors.TRACK_EMPTY);
TreeSet<HistoricalEvent> set = new TreeSet<HistoricalEvent>();
for (SectionEntity se : sections)
set.addAll(se.getEvents());
return set;
}
}
My code is little bit specific. The class uses the field sections just internally to merge all sub-collections. I'm unable to fill sections lazily. I thing, the container expects client to access the field externally via a getter.

Its the problem with life cycle of enties. All enties (track and its sections) must be re-attached to the persistence context. The method collecting events must be in the class using EntityManager. (The entity cannot use the manager to re-attach itself.) Example of updated entity managing class follows:
public class EntityDataAccessor {
#PersistenceUnit(unitName = "someUnit")
private EntityManagerFactory emFactory;
//gets one track
public Track getTrack(int number) {
EntityManager em = emFactory.createEntityManager();
try {
return (Track)em.find(TrackEntity.class, number);
}
finally {
em.close();
}
}
//the new method collecting events
public Collection<HistoricalEvent> getEventsForTrack(TrackEntity te) {
EntityManager em = emFactory.createEntityManager();
te = em.merge(te); //re-attach to the context
Set<SectionEntity> sections = te.getSections();
TreeSet<HistoricalEvent> set = new TreeSet<HistoricalEvent>();
for (SectionEntity se : sections) {
se = em.merge(se); //re-attach to the context
set.addAll(se.getEvents());
}
em.close();
return set;
}
}
See question What's the lazy strategy and how does it work? for more detail.

Related

Entity with CascadeType.ALL in OneToMany not persisting children

I'm working with a 3rd party library provided to our team where one of the entities has a OneToMany relationship to entities of the same type of itself. I've changed the entity name to keep it anonymous.
Probably there's a better way of annotating entities with this type of relationship but as it's provided by a 3rd party I'm avoiding making to many changes so that it's compatible with future patches and updates.
It's using OpenJPA 2.4.0-ep2.0
#Entity
#Table(name = Person.TABLE_NAME)
public class Person {
private Long parentUid;
private List<Person> children = new ArrayList<>();
#OneToMany(targetEntity = Person.class, cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
#ElementJoinColumn(name = "PARENT_UID")
#ElementForeignKey
#ElementDependent
public List<Person> getChildren() {
return this.children;
}
}
When I try to persist a person with children, only the main entity gets persisted and children ignored.
However, if I change the fetch attribute to FetchType.EAGER it works (it persists both the parent and children). My understanding was that the fetch type only affects the loading, not the inserting. Any ideas why is it happening?
Also, is there a way of making it work while keeping the fetch type to FetchType.LAZY?
I've tried the following (modify the setter):
protected void setChildren(final List<Person> children) {
if (Objects.nonNull(children)) {
for (Person child : children) {
child.setParentUid(parentUid);
}
this.childItems = children;
} else {
this.childItems = new ArrayList<>();
}
}
Problem is in the child entity ,you should use #ManyToOne annotation in child entity.
add following code to Person :
public class person {
.
.
#MantToOne(fetch=FetchType.LAZY)
#JoinClolumn(name="PARENT_UID")
private Person parent;
public void setParent(Person parent){
}
.
.
}
then revise setChildren Code like this:
protected void setChildren(final List<Person> children) {
if (Objects.nonNull(children)) {
for (Person child : children) {
child.setParent(this);
}
this.childItems = children;
} else {
this.childItems = new ArrayList<>();
}
}
one important point is ،always fetch type must be sync in parent and child.

Entity Framework 5 - Immediately refresh DbContext after saving changes

I have an MVC application that uses Entity Framework 5. In few places I have a code that creates or updates the entities and then have to perform some kind of operations on the updated data. Some of those operations require accessing navigation properties and I can't get them to refresh.
Here's the example (simplified code that I have)
Models
class User : Model
{
public Guid Id { get; set; }
public string Name { get; set; }
}
class Car : Model
{
public Guid Id { get; set; }
public Guid DriverId { get; set; }
public virtual User Driver { get; set; }
[NotMapped]
public string DriverName
{
get { return this.Driver.Name; }
}
}
Controller
public CarController
{
public Create()
{
return this.View();
}
[HttpPost]
public Create(Car car)
{
if (this.ModelState.IsValid)
{
this.Context.Cars.Create(booking);
this.Context.SaveChanges();
// here I need to access some of the resolved nav properties
var test = booking.DriverName;
}
// error handling (I'm removing it in the example as it's not important)
}
}
The example above is for the Create method but I also have the same problem with Update method which is very similar it just takes the object from the context in GET action and stores it using Update method in POST action.
public virtual void Create(TObject obj)
{
return this.DbSet.Add(obj);
}
public virtual void Update(TObject obj)
{
var currentEntry = this.DbSet.Find(obj.Id);
this.Context.Entry(currentEntry).CurrentValues.SetValues(obj);
currentEntry.LastModifiedDate = DateTime.Now;
}
Now I've tried several different approaches that I googled or found on stack but nothing seems to be working for me.
In my latest attempt I've tried forcing a reload after calling SaveChanges method and requerying the data from the database. Here's what I've done.
I've ovewrite the SaveChanges method to refresh object context immediately after save
public int SaveChanges()
{
var rowsNumber = this.Context.SaveChanges();
var objectContext = ((IObjectContextAdapter)this.Context).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, this.Context.Bookings);
return rowsNumber;
}
I've tried getting the updated object data by adding this line of code immediately after SaveChanges call in my HTTP Create and Update actions:
car = this.Context.Cars.Find(car.Id);
Unfortunately the navigation property is still null. How can I properly refresh the DbContext immediately after modifying the data?
EDIT
I forgot to originally mention that I know a workaround but it's ugly and I don't like it. Whenever I use navigation property I can check if it's null and if it is I can manually create new DbContext and update the data. But I'd really like to avoid hacks like this.
class Car : Model
{
[NotMapped]
public string DriverName
{
get
{
if (this.Driver == null)
{
using (var context = new DbContext())
{
this.Driver = this.context.Users.Find(this.DriverId);
}
}
return this.Driver.Name;
}
}
}
The problem is probably due to the fact that the item you are adding to the context is not a proxy with all of the necessary components for lazy loading. Even after calling SaveChanges() the item will not be converted into a proxied instance.
I suggest you try using the DbSet.Create() method and copy across all the values from the entity that you receive over the wire:
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
this.Context.Entry(newEntry).CurrentValues.SetValues(obj);
return newEntry;
}
UPDATE
If SetValues() is giving an issue then I suggest you try automapper to transfer the data from the passed in entity to the created proxy before Adding the new proxy instance to the DbSet. Something like this:
private bool mapCreated = false;
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
if (!mapCreated)
{
Mapper.CreateMap(obj.GetType(), newEntry.GetType());
mapCreated = true;
}
newEntry = Mapper.Map(obj, newEntry);
this.DbSet.Add(newEntry;
return newEntry;
}
I use next workaround: detach entity and load again
public T Reload<T>(T entity) where T : class, IEntityId
{
((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
return _dbContext.Set<T>().FirstOrDefault(x => x.Id == entity.Id);
}

Can Entity Framework be the "the model" in a Catel Framework?

Hoping someone could clear things up. In the following ViewModel, does using Entity Framework as my model eliminate the need to use [Model] and [[ViewModelToModel(...)] attributes? The code runs the same with or without them, because the binding in the view ignores them and binds to the ObservableCollection.
Comments?
public class MainWindowViewModel : ViewModelBase
{
Models.OneHour_DataEntities ctx;
public MainWindowViewModel()
: base()
{
Save = new Command(OnSaveExecute, OnSaveCanExecute);
ctx = new Models.OneHour_DataEntities();
Customers = new ObservableCollection<Models.Customer>(ctx.Customers);
}
public ObservableCollection<Models.Customer> Customers
{
get { return GetValue<ObservableCollection<Models.Customer>>(CustomersProperty); }
set { SetValue(CustomersProperty, value); }
}
public static readonly PropertyData CustomersProperty = RegisterProperty("Customers", typeof(ObservableCollection<Models.Customer>), null);
public Command Save { get; private set; }
private bool OnSaveCanExecute()
{
return true;
}
private void OnSaveExecute()
{
ctx.SaveChanges();
}
}
Catel uses different interfaces to take advantage of the models. For example, it uses the following interfaces:
IEditableObject => undoing changes to model when user cancels
INotifyPropertyChanged => update view model when model updates
If your entity model implements these interfaces, you can define a property as a model.
In your example however, you use an ObservableCollection (thus a list of models) as a model. That is not supported (or, again, the collection must support IEditableObject and INotifyPropertyChanged).

Decoupling Entity Framework from my POCO classes

I'm dynamically creating my DbContext by iterating over any entities that inherit from EntityBase and adding them to my Context:
private void AddEntities(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var entityTypes = assembly.GetTypes()
.Where(x => x.IsSubclassOf(typeof(EntityBase)) && !x.IsAbstract);
foreach (var type in entityTypes)
{
dynamic entityConfiguration = entityMethod.MakeGenericMethod(type).Invoke(modelBuilder, new object[] { });
EntityBase entity = (EntityBase)Activator.CreateInstance(type);
//Add any specific mappings that this class has defined
entity.OnModelCreating(entityConfiguration);
}
}
}
That way, I can have many namespaces but just one generic repository in my base namespace that's used everywhere. Also, in apps that make use of multiple namespaces, the base repository will already be setup to use all the entities in all the loaded namespaces. My problem is, I don't want to make EntityFramework.dll a dependency of every namespace in the company. So I'm calling OnModelCreating and passing the EntityTypeConfiguration to the class so it can add any mappings. This works fine and here's how I can add a mapping to tell the model that my "Description" property comes from a column called "Descriptor":
class Widget... {
public override void OnModelCreating(dynamic entity)
{
System.Linq.Expressions.Expression<Func<Widget, string>> tmp =
x => x.Description;
entity.Property(tmp).HasColumnName("Descriptor");
}
The good thing is, my entity class has no reference to EF, this method is only called once, when the context is created and if we scrap EF and go to something else in the future, my classes won't have all sorts of attributes specific to EF in them.
The problem is, it's super ugly. How can I let the model know about column mappings and keys in a simpler way than creating these Expressions to get properties to map without hard coding references to EF all over my poco classes?
You could define your own Attributes and use these to control the configuration within OnModelCreating(). You should be able to gain (using reflection) all the details you need for column mapping in one linq query a second query for the creation of the key.
public class DatabaseNameAttribute : Attribute
{
private readonly string _name;
public DatabaseNameAttribute(string name)
{
_name = name;
}
public string Name
{
get
{
return _name;
}
}
}
public class KeySequenceAttribute : Attribute
{
private readonly int _sequence;
public KeySequenceAttribute(int sequence)
{
_sequence = sequence;
}
public int Sequence
{
get
{
return _sequence;
}
}
}
[DatabaseName("BlogEntry")]
public class Post
{
[DatabaseName("BlogId")]
[KeySequence(1)]
public int id { get; set; }
[DatabaseName("Description")]
public string text { get; set; }
}

Entity framework CTP5 Violation of PRIMARY KEY constraint

i have started learning entity framework CTP5 by writing a windows application.
i have two models (Unit and Good) as following:
public class Unit : BaseEntity
{
public Unit()
{
Goods = new List<Good>();
}
public string name { get; set; }
public virtual ICollection<Good> Goods { get; set; }
}
public class Good : BaseEntity
{
public Int64 code { get; set; }
public string name { get; set; }
public virtual Unit Unit { get; set; }
}
i'm using a repository inteface named IRepository as below :
public interface IRepository
{
BaseEntity GetFirst();
BaseEntity GetNext(Int32 id);
BaseEntity GetPrevoius(Int32 id);
BaseEntity GetLast();
BaseEntity GetById(Int32 id);
void Update(int id, BaseEntity newEntity);
void Delete(int id);
void Insert(BaseEntity entity);
int GetMaxId();
IList GetAll();
}
every model has its own repository but maybe it is better to use a generic repository of BaseEntity type. A reference of GoodRepository is made in GoodForm and appropriate object of Good type is made by Activator object in common form methods like Insert/Update/Delete... as below :
private void InsertButton_Click(object sender, EventArgs e)
{
Unit unit = goodRepo.GetUnitById(Convert.ToInt32(UnitIdTextBox.Text));
if (unit == null)
{
unit = new Unit { Id = goodRepo.GetUnitMaxId(), Name = "Gram" };
}
var good = Activator.CreateInstance<Good>();
good.Id = string.IsNullOrEmpty(IdTextBox.Text) ? goodRepo.GetMaxId() : Convert.ToInt32(IdTextBox.Text);
IdTextBox.Text = good.Id.ToString();
good.Name = NameTextBox.Text;
good.Description = DescriptionTextBox.Text;
good.Unit = unit;
goodRepo.Insert(good);
}
and GoodRepository.Insert method is :
public void Insert(Model.BaseEntity entity)
{
using (PlanningContext context = new PlanningContext())
{
context.Goods.Add(entity as Good);
int recordsAffected = context.SaveChanges();
MessageBox.Show("Inserted " + recordsAffected + " entities to the database");
}
}
My problem is SaveChanges() generate an error "Violation of PRIMARY KEY constraint" and says it can not inset duplicate key in object 'dbo.Units.
but if i move my context to the form which i build Good object and insert it there everything is fine.
Can anybody guid me how to solve this issue?
thank in advance
The source of your problem is here:
using (PlanningContext context = new PlanningContext())
{
context.Goods.Add(entity as Good);
//...
}
You are adding the Good entity to a newly created and therefore initially empty context. Now, if you add an entity to the context EF will add the whole object graph of related entities to the context as well, unless the related entities are already attached to the context. That means that good.Unit will be put into the context in Added state as well. Since you don't seem to have an autogenerated identity key for the Unit class, EF tries to insert the good.Unit into the DB with the same key which is already in the database. This causes the exception.
Now, you could ad-hoc fix this problem by attaching the Unit to the context before you add a new Good:
using (PlanningContext context = new PlanningContext())
{
context.Units.Attach((entity as Good).Unit);
context.Goods.Add(entity as Good);
//...
}
But I would better rethink the design of your repository. It's not a good idea to create a new context in every repository method. The context plays the role of a unit of work and a unit of work is usually more a container for many database operations which belong closely together and should be committed in a single database transaction.
So, operations like your InsertButton_Click method should rather have a structure like this:
using (var context = CreateSomehowTheContext())
{
var goodRepo = CreateSomehowTheRepo(context); // Inject this context
var perhapsAnotherRepo = CreateTheOtherRepo(context); // Inject same context
Unit unit = goodRepo.GetUnitById(Convert.ToInt32(UnitIdTextBox.Text));
// unit is now attached to context
// ...
good.Unit = unit;
goodRepo.Insert(good); // should use the injected context and only do
// context.Goods.Add(good);
// It doesn't add unit to the context since
// it's already attached
// ...
context.SaveChanges();
}
Here you are working only with one single context and the repositories will get this context injected (in the constructor for instance). They never create their own context internally.
I suspect it's because GetUnitMaxId is returning the same value more than once. Is Id an auto-incrementing identity column? If so, you shouldn't try to make any assumptions about what that value might be in code.
Even if it's not an auto-incrementing identity column, you can only be sure of it's value when all others have been committed to the DB.
As a general design pattern, try to avoid the need to refer to Ids in code before they've been stored. EF can help with this by exploiting navigation properties (inter-entity object references).