Entity Framework Code First TPC Inheritance Self-Referencing Child Class - entity-framework-4.1

I have a problem With Entity Framework and TPC Inheritance With Self-Referencing Child Class.
My Class definitions:
Public Class BaseObject
<Key()>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Overridable Property iId As Integer
<Required()>
Public Property iModUserId As Integer
<ForeignKey("iModUserId")>
Public Property ModUser As User
<Required()>
Public Property dtModDate As DateTime
End Class
Public Class User
Inherits BaseObject
<Required()>
Public Property sFirstName As String
<Required()>
Public Property sLastName As String
End Class
This is my Context
Public Class DBTestContext
Inherits DbContext
Public Property BaseObjects As DbSet(Of BaseObject)
Protected Overrides Sub OnModelCreating(modelBuilder As System.Data.Entity.DbModelBuilder)
modelBuilder.Entity(Of User)().Map(Sub(m)
m.MapInheritedProperties()
m.ToTable("tUsers")
End Sub)
End Sub
End Class
The problem is that Entity Framework sets the Foreign Key (FK) relationship to the table that is created for the "BaseClass", and NOT to the table created for the "User" class.
This is a major problem since nothing is supposed to be inserted to the "BaseClass" table, and therefore the foreign key constraint is violated when I insert a new user:
Dim context As DBTestContext = New DBTestContext()
Dim user1 As User = New User()
user1.iId = 1
user1.iModUserId = 1
user1.dtModDate = DateTime.Now
context.Users.Add(user1)
context.SaveChanges() 'Error occurs here.
The code works perfectly if I remove the inheritance. The code also work if I remove the DBSer for "BaseObject" and then no table is created for BaseObject, wich is good, but then I cannot easily do a search for all "BaseObjects", wich is bad (but not essential).
The problem with this "solution" is that when I add the code below for more class definitions than the a table for the "BaseObject" class reappears (do not know why, can somebody explain?) and the foreign key relationship is once again set to the "BaseObject" table.
Public Class BaseObject
<Key()>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Overridable Property iId As Integer
<Required()>
Public Property iModUserId As Integer
<ForeignKey("iModUserId")>
Public Property ModUser As User
<Required()>
Public Property dtModDate As DateTime
<InverseProperty("BaseObjects")>
Public Overridable Property Group As ICollection(Of Group)
End Class
Public Class User
Inherits BaseObject
<Required()>
Public Property sFirstName As String
<Required()>
Public Property sLastName As String
End Class
<Table("tGroups")>
Public Class Group
Inherits BaseObject
<InverseProperty("Groups")>
Public Overridable Property BaseObjects As ICollection(Of BaseObject)
End Class
Is there any way to fix this so that the foreign key relationship is set to the "User" table (Class), and not to the "BaseObject" table?
I have tried with the below Fluent API, but the result is the same:
modelBuilder.Entity(Of User)().HasRequired(Function(u) u.ModUser).WithMany().HasForeignKey(Function(u) u.iModUserId)

I don't think you can use relation to base class of TPC inheritance. It doesn't make sense and IMHO it cannot work. The relationship must be expressed in database as any other valid relationship = referential constraint and foreign key. How should database manage relation with non existing table? TPC means that there should not be any table for parent entity because whole content of the parent entity is mapped to the table for every child entity so self referencing relation to parent entity cannot be modeled. It would lead to special set of FKs from derived entity to all other derived entities. Because EF doesn't know how to handle it, it fails.
I expect you must either change your design or use TPT inheritance.

Related

Can't get existByProperty to run: "No property student found for type Student!"

I'm trying to write a "existsBy" query but can't make it work. I know there is a existByID in the JpaRepository, but I need to check by the property student_id. I have tried countless ways of writing the function name, but I can't seem to make it right.
public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private long student_id;
+other fields and getters and setters...
#Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
boolean existsByStudentid(Long student_id);
}
Error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property student found for type Student!
Spring Data is using the underscore as a reserved character. I think it is not possible to use it this way. I think there is no other option to rename the variable.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
So the field must be named with the following convention
private long studentId;
(The underscore can be used for traversing nested properties: To resolve this ambiguity you can use _ inside your method name to manually define traversal points.)
The name of your property is student_id
So the query method should be
#Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
boolean existsByStudent_id(Long student_id);
}
Btw: _ in property name is not a recommended style in Java expect for constants

Losing id (primary key) value stored in a List from DAO to Business through an EJB

I want to display a list of users in an XHTML page. I'm sending a request from my managedBean to the Business through an EJB (3.0) then another request to the DAO still through an EJB (3.0). I'm using JPA 2 and a MySQL database with an entity manager.
I'm sending the following request to my database
#Remote(IDaoUser.class)
#Stateless
Public class DaoUser implements IDaoUser
#PersitenceContext(unitName = "persistence_unit")
Private EntityManager em;
#Override
Public List<User> getAll() {
Query query = em.createQuery("SELECT u FROM User u");
List<User> users = query.getResultList();
return users;
}
At that point everything's fine and I get all my users in my list with all attributes especially id (primary key). My user class has one attribute (birthdate) and inherits from a super class Parent (name, forename, mail, phone...) which inherits again from another class called Who which has just one attribute called id (the primary key).
Then I return my List (users) to the business through an EJB and once in the business I realise all the id attributes are 0. I kept all the others attributes (name, forename...) except ids and I can't figure out why I'm losing these values.
There are well stored in my List in the DAO but all changed to 0 once in the business.
Here is my business class which is very simple
#Remote(IBusinessUser.class)
#Stateless
Public class BusinessUser implements IBusinessUser
#EJB
private IDaoUser proxy;
#Override
Public List<User> getAll() {
List<User> users = proxy.getAll();
return users;
}
Given the description of the problem, I would ask some questions
Are the parent classes entities themselves, i.e. are they annotated with #Entity.
You need to ensure that #Id is on your primary key. Do you have the #Id annotation on the primary key.
Experience has taught me to always have the #Id attribute in class or at least in a parent class tagged with the #MappedSuperclass. And some people still have problems with retrieving their id fields from the mapped super class.
So see the JEE documentation for using the MappedSuperclass annotation. This may require changing your object inheritance model but that shouldn't be a problem given your requirement.
Thanks for your help. Actually both parent classes are themselves entities. My super class "Who" is the only one having an id attribute (primary key) with the annotation #Id. I can't tag my super class with the #MappedSuperclass since it is associated to another class.

"org.hibernate.ObjectNotFoundException: No row with the given identifier exists" but it does exists

I'm using hibernate for my web service.
I'm able to list all the records, but unable to get just one.
The table contains:
ID (VARCHAR) VALUE(BIT)
celiac 1
rate 1
suggestions 0
The error shown is:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.pfc.restaurante.models.Device#id="xxxxxx"]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
And the main code:
#JsonAutoDetect
#Entity
#Table(name = "SETTINGS")
public class Settings implements Serializable{
#Id
#Column(name="ID")
private String id;
#Column(name="VALUE", nullable=false)
private boolean value;
(...)
}
//////////////////7
#Controller
#RequestMapping("/settingsService")
public class SettingsServiceController {
#Autowired
SettingsService settingsService;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody Settings find(#PathVariable("id") String id){
return settingsService.find(id);
}
(...)
}
I've read around that it could be because DB incongruence with my entity (some nullable = true when it shouldn't), but I've checked it already and there is no such a thing.
Could someone lend me a hand?
Thanks in advance!
Your error refers to an entity named 'Device' but your code shows an entity 'Settings'. Are they the same?
I've seen this error only in 2 situations:
The main entity does not exist in the DB and Session.load() is used. Use Session.get() and check for null instead.
Broken relationships. Consider this: EntityA owns a relation to EntityB. EntityB is deleted while the FK in EntityA is left untouched. So, whenever HB tries to load the link A-B the error happens. This can happen when running a normal search or even when saving/refreshing EntityA (HB needs to refresh the link as well).

UNIQUE KEY generated from nowhere

I have 2 tables with #ManyToMany relation field. In hibernate cfg i have
<property name="hbm2ddl.auto">update</property>
Table which is created during application startup has UNIQUE key set on PartId column, which is
#JoinColumn(name="PartId")}
in #ManyToMany relation. I didn't set anywhere that this column should have unique key. Is this the default auto creation behaviour?
The DB is MySQL 5.5
Thanks.
UPD:
Full field desc is:
#ManyToMany
#JoinTable(name="Part_Dev",
joinColumns={#JoinColumn(name="PartId")},
inverseJoinColumns= {#JoinColumn(name="DevCode")})
public List<Dom> getDom() { return dom; }
UPD 2
sorry, I see I didn't mention it. Unique key in Parts table,
#Entity #Table(name="Parts")
public class Parts implements Serializable{
#ManyToMany
#JoinTable(name="Part_Dev",
joinColumns={#JoinColumn(name="PartId")},
inverseJoinColumns= {#JoinColumn(name="DevCode")})
public List<Dom> getDom() {
return dom; }
#Column(name="PartId")
public Integer getPartId() {
return partId; }
you need to specify #JoinTable to make it happen. For instance, if you have two entities : Employee and Project in a many-to-many relationship. You need to have #JoinTable on one side.
#Entity
public class Employee {
#Id private int id;
private String name;
#ManyToMany
#JoinTable(name="EMP_PROJ",
joinColumns=#JoinColumn(name="EMP_ID"),
inverseJoinColumns=#JoinColumn(name="PROJ_ID"))
private Collection<Project> projects;
So, as Chris told, that was the way to identify each part.

Persisting a Many-to-Many entity by adding to a List of entities

I am getting a list of entities and attempting to add more values to it and have them persist to the data base... I am running into some issues doing this... Here is what I have so far...
Provider prov = emf.find(Provider.class, new Long(ID));
This entity has a many to many relationship that I am trying to add to
List<Organization> orgList = new ArrayList<Organization>();
...
orgList = prov.getOrganizationList();
So I now have the list of entities associated with that entity.
I search for some entities to add and I place them in the orgList...
List<Organization> updatedListofOrgss = emf.createNamedQuery("getOrganizationByOrganizationIds").setParameter("organizationIds", AddThese).getResultList();
List<Organization> deleteListOfOrgs = emf.createNamedQuery("getOrganizationByOrganizationIds").setParameter("organizationIds", DeleteThese).getResultList();
orgList.addAll(updatedListofOrgss);
orgList.removeAll(deleteListOfOrgs);
As you can see I also have a list of delete nodes to remove.
I heard somewhere that you don't need to call persist on such an opperation and that JPA will persist automatically. Well, it doesn't seem to work that way. Can you persist this way, or will I have to go throught the link table entity, and add these values that way?
public class Provider implements Serializable {
#Id
#Column(name="RESOURCE_ID")
private long resourceId;
...
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="DIST_LIST_PERMISSION",
joinColumns=#JoinColumn(name="RESOURCE_ID"),
inverseJoinColumns=#JoinColumn(name="ORGANIZATION_ID"))
private List<Organization> organizationList;
...//getters and setters.
}
The link table that links together organizations and providers...
public class DistListPermission implements Serializable {
#Id
#Column(name="DIST_LIST_PERMISSION_ID")
private long distListPermissionId;
#Column(name="ORGANIZATION_ID")
private BigDecimal organizationId;
#Column(name="RESOURCE_ID")
private Long resourceId;
}
The problem is that you are missing a cascade specification on your #ManyToMany annotation. The default cascade type for #ManyToMany is no cascade types, so any changes to the collection are not persisted. You will also need to add an #ElementDependent annotation to ensure that any objects removed from the collection will be deleted from the database. So, you can change your Provider implementation as follows:
#ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#ElementDependent
#JoinTable(name="DIST_LIST_PERMISSION",
joinColumns=#JoinColumn(name="RESOURCE_ID"),
inverseJoinColumns=#JoinColumn(name="ORGANIZATION_ID"))
private List<Organization> organizationList;
Since your Provider class is managed, you should not need to merge the entity; the changes should take effect automatically when the transaction is committed.