How to achieve #OneToOne User and role? - jpa

I have the following tables:
+-----------------------------------+
| user |
+------------------------------------
| id | username | role_id |
+----+-------------+----------------+
| 1 | user1 | 1 |
+----+-------------+----------------+
+-----------------------------------+
| role |
+------------------------------------
| role_id | role_name |
+-----------------+-----------------+
| 1 | GUEST |
+-----------------+-----------------+
| 2 | USER |
+-----------------+-----------------+
Each user have 1 role. Fields looks like this:
User.java
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="ROLE_ID")
private Role role;
Role.java
#Id
#GeneratedValue
#Column(name = "ROLE_ID", unique = true, nullable = false)
private int id;
The Role table will already be populated with only 2 roles. When I try to add a new user, a new record gets saved inside Role table. This is not the behavior I'm seeking. I don't want values to be written to Role table (unless maybe the the value being added doesn't exist in the table).
Any idea how to achieve this properly? Pretty sure this is simple, but I'm new to JPA.
edit: Code to add user
user = new User("JoDoe",new Role("USER"));
user = repository.save(user);
So as pointed out by the comments, it's because I'm creating a new Role every time. Can anybody point out the proper flow please? Should I first retrieve a Role from the db via RoleDao for example, and then assign that object to the new user? Or is there any other way that I'm missing?

First of all, since several users can have the same role, you don't have a OneToOne, but a ManyToOne.
Second, if you create a new Role, without even assigning it an ID, and create a User with that new Role, since the association has cascade = ALL, JPA will indeed create persist this new Role. How is it supposed to know that what you actually want to do is find the role which has the same name in database and assign that role to the new User?
That's what you must do. Assuming you have the ID of the role of the new User, what you need is
Role existingRole = em.getReference(roldId);
User user = new Role(existingRole);
em.persist(user);

Related

#Transient annotation, #org.springframework.data.annotation.Transient annotation, transient keyword and password storing

Currently I'm learning the Spring framework, mainly focusing on it's Security Module. I've watched some guides in connection with registration and login. I saw this common usage of transient keyword or #Transient annotation on the password field in the User class.
My dummy app is using Spring Boot + Spring MVC + Spring Security + MySQL.
I know that
Java's transient keyword is used to denote that a field is not to be serialized.
JPA's #Transient annotation...
...specifies that the property or field is not persistent. It is used to annotate a property or field of an entity class, mapped superclass, or embeddable class.
and the org.springframework.data.annotation's #Transient annotation...
Marks a field to be transient for the mapping framework. Thus the property will not be persisted and not further inspected by the mapping framework.
In my MySQL db I have my spring_demo schema which has 3 tables:
+-----------------------+
| Tables_in_spring_demo |
+-----------------------+
| role |
| user |
| user_role |
+-----------------------+
When I'm using the transient keyword on the password field int the User class, it would not be stored in the MySQL db. (example: test01)
mysql> select * from user;
+----+--------+------------------+----------+
| id | active | email | username |
+----+--------+------------------+----------+
| 1 | 1 | test01#gmail.com | test01 |
+----+--------+------------------+----------+
1 row in set (0,00 sec)
When I'm using the javax.persistence #Transient annotation on the password field in the User class, it also would not be stored in the MySQL db. (example: test02)
But... when I'm using the org.springframework.data.annotation #Transient annotation on the password field in the User class it does stored in the MySQL db. (example: test03) Why is that?
mysql> select * from user;
+----+--------+------------------+----------+--------------------------------------------------------------+
| id | active | email | username | password |
+----+--------+------------------+----------+--------------------------------------------------------------+
| 1 | 1 | test02#gmail.com | test02 | |
| 2 | 1 | test03#gmail.com | test03 | $2a$10$UbvmdhfcKxSNr/I4CjOLtOkKGX/j4/xQfFrv3FizxwEVk6D9sAoO |
+----+--------+------------------+----------+--------------------------------------------------------------+
2 rows in set (0,00 sec)
My main questions are, when I'm using the spring.data based #Transient annotation the password field has persisted. Why? And why should I use any #Transient annotation on a password field?
Thank you for your guidance and help in advance!
Within the Spring Framework you can use Mapping Framework to convert from one form to another. Say for example your spring java server side application needs send to user information to a client (webpage,mobile app) in JSON format.
#Entity
public class User {
#Id
private long id;
#Column(name = "username")
private String username;
#Column(name = "email")
private String email;
#Column(name = "password")
private String password;
}
Now to map this java entity object to JSON format you can either use a mapping framework (e.g jackson: com.fasterxml.jackson.databind.ObjectMapper) or do it manually.
The JSON format output that you would get when to convert user 2 object to JSON is:
{
"id": 2,
"email": "test03#gmail.com",
"username": "test03",
"password": "$2a$10$UbvmdhfcKxSNr/I4CjOLtOkKGX/j4/xQfFrv3FizxwEVk6D9sAoO"
}
Now if you added :
#org.springframework.data.annotation.Transient
#Column(name = "password")
private String password;
and then used the Mapping Framework to again generate the JSON for the user 2 entity you would get:
{
"id": 2,
"email": "test03#gmail.com",
"username": "test03",
}
Note the password field is missing from you JSON output. Thats because #org.springframework.data.annotation.Transient specifically states to the spring framework that the Object Mapper you are using should not include this value when converting from Java Object to JSON.
Also note if you attempted to persist the above entity into the database, it would still save it to the database because #org.springframework.data.annotation.Transient only applys to Object mapping frameworks not JPA.
So to recap:
transient is for all serializations (over the wire, saving to disk, saving to db)
javax.persistence.Transient is specifically for JPA DB serialization
#org.springframework.data.annotation.Transient is for ObjectMapping Framework serializations used within Spring

Why can't EF handle two properties with same foreign key, but separate references/instances?

Apparently, EF6 doesn't like objects that have multiple foreign key properties that use the same key value, but do not share the same reference. For example:
var user1 = new AppUser { Id = 1 };
var user2 = new AppUser { Id = 1 };
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
When I attempt to insert this record, EF throws this exception:
Saving or accepting changes failed because more than one entity of type
'AppUser' have the same primary key value. [blah blah blah]
I've discovered that doing this resolves the issue:
var user1 = new AppUser { Id = 1 };
var user2 = user1; //same reference
I could write some helper code to normalize the references, but I'd rather EF just know they're the same object based on the ID alone.
As for why EF does this, one explanation could be that its trying to avoid doing multipe CRUD operations on the same object since separate instances of the same entity could contain different data. I'd like to be able to tell EF not to worry about that.
Update
So it's as I suspected per my last paragraph above. In absense of a means to tell EF not to do CRUD on either instance, I will just do this for now:
if (address.ModifiedBy.Id == address.CreatedBy.Id)
{
address.ModifiedBy = address.CreatedBy;
}
Works well enough so long as I am not trying to do CRUD on either.
Update2
I've previously resorted to doing this to prevent EF from validating otherwise-required null properties when all I need is the child entity's ID. However, it doesn't keep EF from going into a tizzy over separate instances with the same ID. If it's not going to do CRUD on either AppUser object, why does it care if the instances are different?
foreach (var o in new object[] { address.ModifiedBy, address.CreatedBy })
{
db.Entry(o).State = EntityState.Unchanged;
}
If you get AppUser from context, then you will not need to do anything, because Entity Framework will track entities:
var user1 = context.AppUsers.Find(1);
var user2 = context.AppUsers.Find(1);
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
Now, they both will point to same objects and will not cause to conflict.
You can add two extra properties to have the Id for the main objects which is the AppUser, then you can use only one AppUser object and reference it for both the created and modified by properties.
CreatedById = user1.Id,
ModifiedById = user1.Id
Otherwise, your code will end up by saving two instances of AppUser with the same primary key.
Another approach is to set both the foreign key properties to only one AppUserobject
The explanation is that EF's change tracker is an identity map. I.e. a record in the database is mapped to one, and only one, CLR object.
This can be demonstrated easily by trying to attach two objects with the same key:
context.AppUsers.Attach(new AppUser { Id = 1 });
context.AppUsers.Attach(new AppUser { Id = 1 });
The second line will throw an exception:
Attaching an entity of type 'AppUser' failed because another entity of the same type already has the same primary key value.
This also happens if you assign
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
Somewhere in the process, user1 and user2 must be attached to the context, giving rise to the exception you get.
Apparently, you have a function that receives two Id values that can be different or identical. Admittedly, it would be very convenient if you could simply create two AppUser instances from these Ids, not having to worry about identical keys. Unfortunately, your solution ...
if (address.ModifiedBy.Id == address.CreatedBy.Id)
... is necessary. Solid enough, though.

JPA recursive entity StackOverflowError

I have a User entity generated in Netbeans from an existing database table. The table has a column lastUpdatedByUser that is a User entity. Most of the tables in this database have a lastUpdatedByUser column and queries against those entities correctly return a user object as part of the result.
Ex. Retrieve FROM ProductionTable WHERE date = 'someDate' has a lastUpdatedByUser object that shows who last updated the table row and the rest of their user attributes.
If the productionTable data is edited in the web-app and submitted I need to update the lastUpdatedByUser column.
Users userUpdating = usersService.selectUserEntityByUserId(userId);
Users userEntity = usersFacade.findSingleWithNamedQuery("Users.findByUserId", parameters);
SELECT u FROM Users u WHERE u.userId = :userId
returns a User object that contains a lastUpdatedByUser that is a User object that contains a lastUpdatedByUser that is a User object that contains a lastUpdatedByUser object.... (I have no clue how many there are, and twenty rows of these adds up)
After I persist this
productionEntity.setLastUpdatedByUser(userUpdating);
I get Json StackOverflowError in the next request for the updated entity
gson.toJson(updatedProductionEntity)
The Users entity definition:
#OneToMany(mappedBy = "lastUpdatedByUser")
private Collection<Users> usersCollection;
#JoinColumn(name = "LastUpdatedByUser", referencedColumnName = "UserId")
#ManyToOne
private Users lastUpdatedByUser;
#OneToMany(mappedBy = "lastUpdatedByUser")
private Collection<Production> productionCollection;
How can edit that such that I continue to get a user object as part of other entities like Production, but only a single lastUpdatedByUser object for a User entity?
Thanks for any insight.
I'm guessing this is my issue:
#JoinColumn(name = "LastUpdatedByUser", referencedColumnName = "UserId")
as I found a FK in the Users table to its own UserId
Love refactoring
================================
Drop that FK from the Users table and regenerate the entity in Netbeans and I get
private Integer lastUpdatedByUser;
like it should be
instead of
private Users lastUpdatedByUser;
Now I get to edit all the entities that have valid FKs into the Users table and code and...
Thanks for listening.

Hibernate Envers - Adding Historical Data

Is there a way to add revision straight into the _AUD tables as a historical revision?
As I understand it as and when an entity is persisted envers creates a revision record in the _AUD table and keeping the current record in the entity table. Suppose I would like to add historical revision and not affect the entity table, is this possible?
For example, I have a Person entity
PERSON
ID | NAME
1 | SMITH
PERSON_AUD
ID | REV | REVTYPE | NAME
1 | 1 | 0 | SMITH
I would like to add the following in PERSON_AUD without modifying the PERSON table as SMITH is the current name.
PERSON_AUD
ID | REV | REVTYPE | NAME
1 | 1 | 0 | SMITH
1 | 2 | 2 | JONES
I'm afraid that's not possible with the current Envers API.
My background is C# but since the frameworks for Java are more powerful (at least I think this) I am sure you will find the related methods.
1) Reattaching the entity to the session will create a new revision:
private void Reattach(Person person)
{
_sessionContainer.Session.Transaction.Begin();
_sessionContainer.Session.Evict(person);
_sessionContainer.Session.Update(person);
_sessionContainer.Session.Flush();
_sessionContainer.Session.Transaction.Commit();
}
2) If you want to manipiulate the audit entry you need to append a listener (in this case for preupdate):
configuration.AppendListeners(ListenerType.PreUpdate, new object[] { new PreUpdateListener() });
where the implementation is doing the magic:
public class PreUpdateListener : IPreUpdateEventListener
{
public bool OnPreUpdate(PreUpdateEvent ev)
{
var person = ev.Entity as Person;
if (person != null)
{
person.Name = "Jones";
}
return true;
}
}
Please let me know if this approach is working for you.
You seem to be forgetting or not aware of the fact that revision number is global; it is tracked in a separate table which is called REVINFO in the default setup. Each entry for a revision number is accompanied by a timestamp as well. It is simply wrong (might even become a serious situation depending on the environment) to be inserting historical data that wasn't created naturally.
If you still want to do this, you can use native SQL to do it by creating a revision entry in REVINFO first and then using that in your audit table. Which is wrong.

how to insert multiple values using entity framework..?

I'm using Silverlight5 with MVVM framework and Entity framework. In my project i have one doubt..
I have an entity named 'Customer' the structure is as follows..
Customer ocustomer = new Customer();
ocustomer.CustomerName = customername;
ocustomer.CompanyName = company;
ocustomer.AddressLine1 = address1;
ocustomer.AddressLine2 = address2;
ocustomer.City = city;
ocustomer.State = state;
ocustomer.Country = country;
ocustomer.ZipCode = zipcode;
ocustomer.Notes = note;
_context.Customers.Add(ocustomer);
Now my need is to insert the Customer value into another table Named Customer_Entity
Customer_Entity ocustomerEntity=new Customer_Entity ();
ocustomerEntity.CustomerID=Customer_ID;
ocustomerEntity.EntityTypeID =1;
.
.
.
.
ocustomerEntity.CreatedTime =System.DateTime.Now;
ocustomerEntity.CreatedBy=Common.ActiveData.Instance.userid;
Here my need is to insert the customer value to Customer_Entity in every single row..
Customer_Entity Table Structure is as follows,
EntityID| CustomerID| EntityValue|
----------------------------------
1 | 22 |jasper |
2 | 22 |Company:Raj |
3 | 22 |Address |
ocustomer.CustomerName=customername
.
.
.
ocustomer.CreatedTime=system.DateTime.Now..
so i need to insert all the values in every single row using unique CustomerID..
Need help to solve this one..
On your CustomerEntity object you should have a navigation property pointing to the corresponding Customer record. Similarly, I would expect that your Customer class has a collection of CustomerEntity on it as well.
In which case, you should instantiate the Customer object, populating with the necessary information. Don't add it to the DbSet yet, though. Afterwards, create all of the CustomerEntity records that you need to, connecting it to the Customer object by using the navigation property itself (i.e. NOT the ID field), and adding that CustomerEntity record to the corresponding DbSet in you Context class.
As a final step add your Customer object to the corresponding DbSet, and run SaveChanges. You should be good to go. Entity Framework should automatically generate the ID for you and populate it to the CustomerEntity records.