Regarding relationship management in JPA2 - orm

I am getting confused about managing entities in a relationship. According to the book PRO JPA2, relationship should be manually configured and assigned at both ends of the relationship.
Now, consider this relationship.
#Entity
public class Employee {
..
#ManyToOne(cascade=CascadeType.PERSIST)
private Department department;
..
}
#Entity
public class Department {
..
#OneToMany(mappedBy="department")
private Set<Employee> employees = new HashSet<Employee>();
public void addEmployee(Employee e){
getEmployees().add(e);
e.setDepartment(this);
}
}
I made a simple test case that involves this line of code to verify this.
Department dept1 = new Department();
dept1.setName("MARKETING");
Employee e1 = new Employee();
e1.setName("JOHN DOE");
e1.setDepartment(dept1); //[1]
//dept1.addEmployee(e1); //[2]
Consider Line [1]:
I thought this would not properly update the Employee table with the correct department ID but I checked Derby and it is able to properly execute the update.
Consider Line [2]:
This is the proper way according to the book.
Eclipselink/Derby/JPA2

If you do it like in line [1], the Employee 'e1' will be stored in database with the FK ok, but... If you get Department dept1 and invoke getEmployees(); Employee 'e1' will not be in this list. Even if you make a new query to get that deparment from database again; it will not have Employee 'e1' (at least not until you reploy the application). That is why you should always do it in way [2].
When I started working with JPA2 I didn't know this; and was persisting entities like in [1]. I almost went crazy trying to determine why the recently persisted entities were not read by JPA, but were actually stored in the DB.

Related

ASP.NET MVC 4 Deferred query execution not retrieving data from database

I am new in ASP.NET MVC 4. In my project I am using Code First technique in of EF. I want to retrieve some data from database and I used following code for this :
List<SelectListItem> ls = new List<SelectListItem>();
var lm = from m in db.BOs //fetch data from database
select m;
foreach (var temp in lm)
{
ls.Add(new SelectListItem() { Text = temp.Name, Value = temp.Id.ToString() });
}
But when execution pointer move inside foreach it immediately come back out of the loop showing return ls value Count = 0. Code does not giving me any error while running that's why I am not getting where is going wrong.
UPDATE: I found something new this problem. When I kept mouse pointer over var lm; it shows me query and in query table name in FROM clause is not that one in my SQL database. My SQL table name is BO and in query it is taking BOes. I don't know from where this name is coming. So How I overcome this??
decorate your BO class with Table("BO") to specify the table name (attribute is in System.ComponentModel.DataAnnotations.Schema namespace)
[Table("BO")]
public partial class BO
{
...
Write following code inside DbContext class :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
The modelBuilder.Conventions.Remove statement in the OnModelCreating method prevents table names from being pluralized. If you didn't do this, the generated tables would be named Students, Courses, and Enrollments. Instead, the table names will be Student, Course, and Enrollment. Developers disagree about whether table names should be pluralized or not. This tutorial uses the singular form, but the important point is that you can select whichever form you prefer by including or omitting this line of code.

Invalid index N for this SqlParameterCollection with Count=N only when associated table has null record

I have a rather complex entity which will not save when a particular database table is missing a record. When the record exists the entity saves correctly. When the record does not I receive the exception:
Invalid index N for this SqlParameterCollection with Count=N
After reading a bunch of solutions found via Google and the most closely related questions on Stack Overflow:
What's causing “Invalid index nn for this SqlParameterCollection
with Count=nn” when a column is Null in the database?
Invalid Index n for this SqlParameterCollection with Count=n” OR “foreign key
cannot be null
I believe my issue has to do with the way I have my mapping files setup. The Customer entity has reference to the Person entity. Person maps to a table which we have read, but not write access to. It is when a record for the Person entity does not exist that I generate the exception. If the record exists no issue. I've set the reference to Person from customer to Nullable(). I have also double checked to ensure I do not have a property mapped twice from either entity.
Here is what I feel is the pertinent mapping information, but can provide more as needed:
Customer
//more mapping code...
References(x => x.Person, "snl_id").Nullable();
//more mapping code...
Person
//more mapping code...
ReadOnly();
Id(x => x.SnlId).Column("SNL_ID");
//more mapping code...
To further complicate matters we have some painful code to make NHibernate perform better when Person does not exist. I am not sure it applies here, but thought it pertinent enough to include in my question. We are using the below code because without it the NHibernate JIRA will create tons of queries. This solution is outlined in this Stack Overflow answer.
Customer's person property
public virtual Person Person
{
get
{
try
{
var snlId = per.Name;
return per;
}
catch
{
return null;
}
}
set
{
per = value;
}
}
private EPerson per;
What am I missing in my mappings that would cause this exception? Is there another piece of this problem that I am not seeing?
While Scott's solution of removing the snl_id property from the Customer class fixes the issue it causes problems that I cannot get around-- the snl_id can exist in the Customer table even there is not a corresponding Person table record. Since that is the case there are times when I will need access to the snl_id when I cannot get to it via the associated Person property.
I considered several alternative solutions but settled on creating a view of the Customer table including the Customer table primary key and the snl_id from the customer table. Then mapping that property via a join to the view.
Join("v_cust_id_snl_id", j => j.KeyColumn("cust_id").Map(x => x.SnlId, "snl_id")
This change allowed me to have my cake and eat it to. I was able to keep the SnlId property on customer, but no longer throw the exception when saving.
Do you have the snl_id referenced as a property in Customer as well as being the primary key for the child object? If so, this is causing the error you are receiving. Remove the property from Customer and use Person to get the value.

Why does NHibernate need to know the ID of an auto ID based entity before flush is called?

With my only ORM knowledge being L2S/EF, I was surprised when the following code inserted a row into the database before I called repo.Save:
var repo = new UserRepository();
var user = new User { Name = "test" }
repo.Add(user);
//repo.Save();
Repo looks like this:
public void Add(T entity)
{
session.Save(entity);
}
public void Save()
{
session.Flush();
}
After some digging, it seems NHibernate needs to make the insert happen right away in order to get the ID of the new entity (since it's using an auto increment ID). But L2S/EF doesn't work like this; I can add many entities and save them all at the end.
Question is: is there a way to achieve the same thing with NHibernate, while still using auto increment IDs, and out of interest does anyone know why it works like this?
Fabio Maulo already blogged about the usage of identity generator a few times. The answer is: use hilo, guid.comb or something like this.
NHibernate needs the identity because every entity in the session (they are called "persistent entities") needs to be identified. The identity is also normally used to determine if the record already exists in the database (unsaved value).
session.Save actually only makes a transient entity persistent. When the database is generating the id, it needs to be stored to get the id. If NH can create the id itself (eg using hilo), it could be stored next time when the session gets flushed.

NHibernate - flagging specific properties as 'dirty'

I am working on an NHibernate project and have a question regarding updating transient entities.
Basically the workflow is as follows:
Create a DTO (projection) and send over the wire to client. This has a small subset of properties from the entity.
Client sends back the changed DTO
Map the DTO properties back onto the appropriate enitity so an UPDATE statement can be generated and executed by NH.
Save the entity
Point 4 is where I have the issue. Currently I can achieve this update using the session.Merge() method, however it must first load the entity from the db (assume no 2LC) before updating. So, both a select and an update statement are fired.
What I would like to do is create a transient instance of the entity, map the new values from the DTO, then have NH generate a SQL statement using only the properties I have changed. The additional select should be unnecessary as I already have the entity ID and the values required for the SET clause. Is this possible in NH?
Currently using session.Update(), all properties will be included in the update statement and an exception is raised due to the uninitialized properties that are not part of the DTO.
Essentially I need a way to specify which entity properties are dirty so only these are included in the update.
== EDIT ==
For example...
public class Person
{
public virtual int PersonId { get; set; }
public virtual string Firstname { get; set; }
public virtual string Nickname { get; set; }
public virtual string Surname { get; set; }
public virtual DateTime BirthDate { get; set; }
}
And the test case.
// Create the transient entity
Person p = new Person()
p.id = 1;
using (ISession session = factory.OpenSession())
{
session.Update(p);
// Update the entity – now attached to session
p.Firstname = “Bob”;
session.Flush();
}
I was hoping to generate a SQL statement similar to ‘UPDATE Persons SET Firstname = ‘Bob’ WHERE PersonID = 1’. Instead I get a DateTime out of range exception due to BirthDate not being initialised. It shouldn’t need BirthDate as it is not required for the SQL statement. Maybe this isn’t possible?
== /EDIT ==
Thanks in advance,
John
Dynamic-update is what you're looking for. In your mapping file (hbm.xml):
<class name="Foo" dynamic-update="true">
<!-- remainder of your class map -->
Be aware of the potential problems that this may cause. Let's say you have some domain logic that says either FirstName or Nickname must not be null. (Completely making this up.) Two people update Jon "Jonboy" Jonson at the same time. One removes his FirstName. Because dynamic-update is true, the update statement just nulls out Jon and the record is now "Jonboy" Jonson. The other simultaneous update removes his Nickname. The intent is Jon Jonboy. But only the null-out of the Nickname gets sent to the database. You now have a record with no FirstName or Nickname. If dynamic-update had been false, the second update would have set it to Jon Jonboy. Maybe this isn't an issue in your situation, but setting dynamic-update="true" has consequences and you should think through the implications.
UPDATE: Thanks for the code. That helped. The basic problem is NHibernate not having enough information. When you say session.Update(p), NHibernate has to associated a disconnected entity with the current session. It has a non-default PK. So NHibernate knows that it's an update and not an insert. When you say session.Update(p), NHibernate sees the whole entity as dirty and sends it to the database. (If you use session.Merge(obj), NHibernate selects the entity from the database and merges obj with it.) This is not what you really mean. You want to associate your object with the current session, but mark it as clean. The API is somewhat non-intuitive. You use session.Lock(obj, LockMode.None) as below.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var p = new Person {PersonId = 1};
session.Lock(p, LockMode.None); // <-- This is the secret sauce!
p.Firstname = "Bob";
// No need to call session.Update(p) since p is already associated with the session.
tx.Commit();
}
(N.B. dynamic-update="true" is specified in my mapping.)
This results in the following SQL:
UPDATE Person
SET Firstname = 'Bob' /* #p0_0 */
WHERE PersonId = 1 /* #p1_0 */

nhibernate - disable automatic\lazy loading of child records for one to many relationsihps

I would like to know if there is a way to disable automatic loading of child records in nHibernate ( for one:many relationships ).
We can easily switch off lazy loading on properties but what I want is to disable any kind of automatic loading ( lazy and non lazy both ). I only want to load data via query ( i.e. HQL or Criteria )
I would still like to define the relationship between parent child records in the mapping file to facilitate HQL and be able to join parent child entities, but I do not want the child records to be loaded as part of the parent record unless a query on the parent record
explicitly states that ( via eager fetch, etc ).
Example:
Fetching Department record from the database should not fetch all employee records from the database because it may never be needed.
One option here is to set the Employees collection on Department as lazy load. The problem with this approach is that once the object is given to the calling API it can 'touch' the lazy load property and that will fetch the entire list from the db.
I tried to use 'evict' - to disconnect the object but it does not seem to be working at all times and does not do a deep evict on the object.
Plus it abstracts the lazy loaded property type with a proxy class that plays havoc later in the code where we are trying to operate on the object via reflection and it encounters unexpended type on the object.
I am a beginner to nHibernate, any pointers or help would be of great help.
Given your request, you could simply not map from Department to Employees, nor have an Employees property on your department. This would mean you always have to make a database hit to find the employees of a database.
Aplogies if these code examples don't work out of the box, I'm not near a compiler at the moment
So, your department class might look like:
public class Department
{
public int Id { get; protected set; }
public string Name { get; set; }
/* Equality and GetHashCode here */
}
and your Employee would look like:
public class Employee
{
public int Id { get; protected set; }
public Name Name { get; set; }
public Department Department { get; set; }
/* Equality and GetHashCode here */
}
Any time you wanted to find Employees for a department, you've have to call:
/*...*/
session.CreateCriteria(typeof(Employee))
.Add(Restrictions.Eq("Department", department)
.List<Employee>();
Simply because your spec says "Departments have many Employees", doesn't mean you have to map it as a bi-directional association. If you can keep your associated uni-directional, you can really get your data-access to fly too.
Google "Domain Driven Design" Aggregate, or see Page 125 of Eric Evan's book on Domain Driven Design for more information
You can have the lazy attribute on the collection. In your example, Department has n employees, if lazy is enabled, the employees will not be loaded by default when you load a department : http://www.nhforge.org/doc/nh/en/#collections-lazy
You can have queries that explicitly load department AND employees together. It's the "fetch" option : http://www.nhforge.org/doc/nh/en/#performance-fetching-lazy