I have a one to many relationship (profile to message). I tried to save a message owned by a certain user/profile. What wrong with the code below?
public Message createMessage(Message msg, String recepient) {
EntityManager em = EMF.get().createEntityManager();
UserAccess access = new UserAccess();
Profile user = access.searchUser(recepient);
msg.setUser(user);
em.getTransaction().begin();
em.persist(msg);
em.getTransaction().commit();
em.close();
return msg;
}
search user method
public Profile searchUser(String displayName){
EntityManager em = EMF.get().createEntityManager();
Profile user;
try{
Query q = em.createNamedQuery("Profile.searchByDisplayName");
q.setParameter("displayName", displayName);
user = (Profile) q.getSingleResult();
} catch(javax.persistence.NoResultException e){
user = null;
}
em.close();
return user;
}
I encountered the error below:
java.lang.IllegalStateException: Primary key for object of type Profile is null.
Can it be because you do not have an "Id", a primary key, defined in your Profile Entity? Or is not set to any value when you created the specific profile?
Also, check if the relationships are defined properly between the Profile and Message entities.
jpa OneToMany & ManyToOne
Related
I have a table with a DATETIME DEFAULT field.
CREATE TABLE People
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
...
DtOccurrence DATETIME DEFAULT getDATE(),
);
Using scaffolding for generate Class and Entitity for Controllers + Views.
Default CRUD working fine, but if I try update a register, [DtOccurrence] get NULL in database.
How fix it? Thanks in advance
Create saving OK
Update only [Name] field send null [DtOccurrence] for database and my auto-generated class dont have this [DtOccurrence] field:
UPDATE:
CONTROLLER Create method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] People people)
{
if (ModelState.IsValid)
{
_context.Add(people);
await _context.SaveChangesAsync();
return RedirectToAction("Edit", "Pessoas", new { people.Id });
}
return View(people);
}
CONTROLLER Edit method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (id != people.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(people);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PeopleExists(people.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(people);
}
Auto-generated class scaffolding
public partial class Pessoa
{
public Pessoa()
{
public int Id { get; set; }
public string Name { get; set; }
}
}
As mentioned in my comment, while your initial request to provide data to the view was given an entity from the DB Context, the object (Person) you get back in your Update method is not the same entity, and is not associated with your DbContext. It is a deserialized copy. Calling Update with it when it does not contain all fields will result in fields getting set to #null. Calling Update with a detached entity like this from a client is also an attack vector for unauthorized updates to your domain. (Debugging tools /plugins can intercept the call to the server and alter the entity data in any number of ways.)
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (!ModelState.IsValid)
return View(people);
var dataPeople = await _context.People.SingleAsync(x => x.id == people.id);
dataPeople.name = people.name;
await _context.SaveChangesAsync(); // dataPeople is a tracked entity and will be saved, not people which is acting as a viewmodel.
return RedirectToAction(nameof(Index));
}
Using Update will generate an update statement where all fields on the entity are overwritten. You may decide to pass an incomplete entity to the view, or an incomplete entity back from the view, but EF has no notion of what data is missing because it wasn't provided/changed, vs. what was cleared out so it updates everything. Instead, you should load the entity from the DbContext based on the ID provided (which will error if the ID is not found) then set the properties you want to change on that tracked entity before calling SaveChanges. This ensures that the resulting SQL update statement contains only the columns you want changed.
As a general rule I recommend using view model classes for communicating models between server and client so it is clear what the data being passed around actually is. Passing entities between server and views is an anti-pattern which is prone to performance problems, serialization issues, and both intentional and accidental data corruption.
Additional validations should include making sure the changes are complete/legal, and potentially checking a row version # or last modified date between the passed model and the data loaded from the DB to ensure they match. When the user opened the page they may have gotten version #1 of the record. When they finally submit the form, if the DB returned version #2, it would indicate that someone else modified that row in that time. (Otherwise you are overwriting the changes)
Hi I´m new using GAE and JPA, and I´m having some problems trying to update an entity. I copy next a code example:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String userName;
#ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
private Address address;
}
When I save on datastore a User instance it's store without problems.
After this, I retrive this instance from the datastore, then I set a new userName and try to update it.
public User updateUser(User user) {
EntityManager mgr = getEntityManager();
if (!containsUser(user)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(user);
}
The update is performed, the new userName is stored in the dataStore, but as the Address field has FetchType.LAZY I'm losing the persisted value on the update.
How can I make an update of some fields without losing other values?
Try getting and saving the entity in the same method, otherwise you will lost the session and the manager will recognize the lazy attributes as null ones.
Hope it helps!
I have a DB from which I have generated the models using Database-First through Entity Framework.
The picture below shows what's in the database:
When I do the following in my code (inserting a user with the associated role):
using (var context = new ApplicationDbContext())
{
var role = new Role {Name = "Admin"};
var user = new User {UserName = username,
PasswordHash = passwordHash,
Roles = new List<Role> { role }};
context.Set<User>().Add(user);
context.SaveChanges(); //Error here
}
It throws the following error:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
and in the InnerException I get:
Invalid object name dbo.RoleUsers
which is pretty strange since I have a table UserRoles, not RoleUsers.
Does anyone knows what the source of the error is here?
Thanks,
Ionut
I am using Google App Engine with JPA to implement a one-to-many bidirectional relationship. Everything works just fine when I debug and test my application on my machine at home, but after I deploy it to the App Engine persistence seems to break down.
This is the model I have (stripped down for simplicity):
User.java:
#Entity
class User implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
#OneToMany(mappedBy = "owner",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER)
private List<Book> books;
public getBooks() { return this.books; }
}
Book.java:
#Entity
class Book implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
#ManyToOne(fetch = FetchType.LAZY)
private User owner;
private String name;
}
To create a new User:
User user = new User()
// This is done just for testing. It works fine.
user.getBooks().add(new Book("TEST"))
EntityManager em = /* ... */
EntityTransaction transaction = em.getTransaction();
try
{
transaction.begin();
em.persist(user);
transaction.commit();
}
/* Exceptions handling. */
finally
{
if (transaction.isActive())
transaction.rollback();
em.close();
}
And to add a book:
User user = /* ... */
Book book = new Book("A new book");
user.getBooks().add(book);
EntityManager em = /* ... */
EntityTransaction transaction = em.getTransaction();
try
{
transaction.begin();
/* user.getBooks().add(book); - placing this here doesn't change anything */
em.merge(user);
transaction.commit();
}
/* Exceptions handling. */
finally
{
if (transaction.isActive())
transaction.rollback();
em.close();
}
What I saw, before I added the 'test' book which is created along with the User, is that creation of the first book entity works just fine, but whenever I create another one, the previous one is somehow removed from the Datastore and is replaced by the new one which I just created (I can tell because of the books' names). So I cannot create more than one book for the same user.
I tried to see if I somehow messed up persistence of the Book entity, and for that reason I added the 'TEST' book. The problem persists, only that now I have the first book ("TEST") and I keep replacing the second book on the list whenever I try to add a new one.
Again, this doesn't happen when I debug my application, only after I deploy.
I tried calling em.persist(book) before the call to em.merge(user), but that caused an exception saying the book's owner was already set when it was persisted and cannot be changed. I tried setting the relationship myself (like in this thread), but that caused a failed transaction when adding a book.
I'm not sure if its relevant, but the type of the 'books' column I see is datastore_types.Key.from_path, as in:
[datastore_types.Key.from_path(u'User', 9001L, u'Book', 1L, _app=u's~myapp'),
datastore_types.Key.from_path(u'User', 9001L, u'Book', 2001L, _app=u's~myapp')]
Any help would be appreciated,
Thank you!
Can you try below:
User user = /* ... */
Book book = new Book("A new book");
book.setOwner(user);
EntityManager em = /* ... */
EntityTransaction transaction = em.getTransaction();
try{
transaction.begin();
em.persist(user);
transaction.commit();
}finally{
if (transaction.isActive())
transaction.rollback();
em.close();
}
In my Profile class I have
#OneToOne(cascade=CascadeType.PERSIST)
private ProfilePicture profilePic = null;
My method in updating the profilePic
public Profile updateUserProfilePic(Profile user) {
EntityManager em = EMF.get().createEntityManager();
em.getTransaction().begin();
Profile userx = em.find(Profile.class, user.getEmailAddress());
userx.setProfilePic( user.getProfilePic() );
em.getTransaction().commit();
em.close();
return userx;
}
When updateUserProfilePic is called, it just add another profilePic in datastore, it doesn't replaced the existing profilePic. Is my implementation correct? I want to update the profilePic of profile.
"Transient" means not persistent and not detached.
Using that version of GAE JPA you need a detached or managed object there if you want it to reuse the existing object.
Using v2 of Googles plugin there is a persistence property that allows merge of a transient object that has "id" fields set.