How to update an object with ManyToMany relation with doctrine 2 - doctrine-orm

Hi there Stack Overflow members!
I have doctrine 2 installed everything work perfectly,
I have generated my entities and proxies with command ,
my problem is when I try to update an entities with many to many relation ship
I had this problem
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
'8-1' for key 'PRIMARY''
it seems that doctrine try to insert a new entities and not trying to update joined table
if there any problem in my code , is there any example clear ?
thanks
//update entities user
$user=$this->em->getRepository('Entities\User')->find((int)$this->input->post('id'));
$user->setNom($this->input->post('nom'));
$user->setPrenom($this->input->post('prenom'));
//update entities services(user have many service)
foreach($this->input->post('services') as $post){
$service = $this->em->getRepository('Entities\Service')->find((int)$post);
if ($service instanceof Entities\Service) {
$user->addService($service);
}
$this->em->flush();

As far as I can see from your example, you are not persisting any entities, therefore Doctrine doesn't register any changes, and flush has nothing to update.
You have to persist the owning side of the relationship.
$this->em->persist($service);
or
$this->em->persist($user);
Depends what did you specify as the owning side.
Check out the documentation

Related

Google Cloud Datastore - get after insert in one request

I am trying to retrieve an entity immediately after it was saved. When debugging, I insert the entity, and check entities in google cloud console, I see it was created.
Key key = datastore.put(fullEntity)
After that, I continue with getting the entity with
datastore.get(key)
, but nothing is returned. How do I retrieve the saved entity within one request?
I've read this question Missing entities after insertion in Google Cloud DataStore
but I am only saving 1 entity, not tens of thousands like in that question
I am using Java 11 and google datastore (com.google.cloud.datastore. package)*
edit: added code how entity was created
public Key create.... {
// creating the entity inside a method
Transaction txn = this.datastore.newTransaction();
this.datastore = DatastoreOptions.getDefaultInstance().getService();
Builder<IncompleteKey> builder = newBuilder(entitykey);
setLongOrNull(builder, "price", purchase.getPrice());
setTimestampOrNull(builder, "validFrom", of(purchase.getValidFrom()));
setStringOrNull(builder, "invoiceNumber", purchase.getInvoiceNumber());
setBooleanOrNull(builder, "paidByCard", purchase.getPaidByCard());
newPurchase = entityToObject(this.datastore.put(builder.build()));
if (newPurchase != null && purchase.getItems() != null && purchase.getItems().size() > 0) {
for (Item item : purchase.getItems()) {
newPurchase.getItems().add(this.itemDao.save(item, newPurchase));
}
}
txn.commit();
return newPurchase.getKey();
}
after that, I am trying to retrieve the created entity
Key key = create(...);
Entity e = datastore.get(key)
I believe that there are a few issues with your code, but since we are unable to see the logic behind many of your methods, here comes my guess.
First of all, as you can see on the documentation, it's possible to save and retrieve an entity on the same code, so this is not a problem.
It seems like you are using a transaction which is right to perform multiple operations in a single action, but it doesn't seem like you are using it properly. This is because you only instantiate it and close it, but you don't put any operation on it. Furthermore, you are using this.datastore to save to the database, which completely neglects the transaction.
So you either save the object when it has all of its items already added or you create a transaction to save all the entities at once.
And I believe you should use the entityKey in order to fetch the added purchase afterwards, but don't mix it.
Also you are creating the Transaction object from this.datastore before instantiating the latter, but I assume this is a copy-paste error.
Since you're creating a transaction for this operation, the entity put should happen inside the transaction:
txn.put(builder.builder());
Also, the operations inside the loop where you add the purchase.getItems() to the newPurchase object should also be done in the context of the same transaction.
Let me know if this resolves the issue.
Cheers!

A new entity was found through the relationship - Doctrine

I have a cached entity Product. I retrieve the entity and update some properties including adding price into it.
Relationship is set up like this
class Product
{
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Price", mappedBy="product", fetch="EXTRA_LAZY")
*/
private $prices;
}
class Price
{
/**
* #var Product
*
* #ORM\ManyToOne(targetEntity="Product", inversedBy="prices")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
}
I am trying to save properties like this.
// Here $em is Entity Manager
$cacheDriver = $em->getConfiguration()->getResultCacheImpl();
$product = $cacheDriver->fetch($key)
$product->setUpdatedAt(new \DateTime());
$price = new Price;
$label = $em->getReference(PriceLabelEntity::class, $labelId);
$price->setLabel($label)
->setPriceLabelId($labelId)
->setProduct($productEntity)
->setProductId($product->getId());
$em->persist($price);
$product->addPrice($price);
$em->flush();
But whenever I do that I get the exception saying.
A new entity was found through the relationship 'Price#product' that was not
configured to cascade persist operations for entity:
Product#0000000043c0cdc400007fec6b41ca76. To solve this issue: Either
explicitly call EntityManager#persist() on this unknown entity or configure
cascade persist this association in the mapping for example
#ManyToOne(..,cascade={"persist"}). If you cannot find out which entity
causes the problem implement 'Product#__toString()' to get a clue.
Even more strange issue if I do just this.
$product->setUpdatedAt($timeStamp);
$em->flush();
It does not throw any error but no data is saved in DB. Not sure whether this issues are related or not either.
I tried to put cascade too but it gives different error. Is there any way to resolve this issue.
It is my understanding that Doctrine entity managers are fairly adept (at least, one would hope) at managing their result cache, so fiddling with it directly may be the issue.
Instead of hitting the result cache directly, try using:
$product = $entityManager->find('Product',$key);
Then, you also seem to have a typo on the last line - missing an "e" in "entityManager" - but I am sure that would throw an error, so it's probably a copy/paste issue when creating this question.
EDIT: You are also using both $product and $productEntity. If these are meant to be the same variable, you should pick one name and stick with it.
#Rushing thank you very much for your reply. You are right there are some typos because I had to do some changes in code to paste in StackOverflow but the actual code is correct. I am sorry I was bit late to reply on this topic. The solution you provided was good but even better solution was to use merge like this.
$productEntity = $em->merge($productEntity);
....rest of the logic
In this way we did not have to query the database again and entity was fresh.

NHIbernate parent-child issue: foreign key set to null instead of deletion

Searched high and low for an answer but can't make this work with all the examples I've tried.
I have a certain bi directional parent child relationship.
Parent ("Case") holds a collection of children ("CaseNotes") and is mapped as follows:
HasMany<CaseNote>(c => c.Notes).KeyColumn("casesid").Inverse().Cascade.AllDeleteOrphan()
Children reference parent:
References<Case>(c => c.ParentCase,"casesid").Cascade.All();
On Add, the item is added to collection but is not persisted to the DB until explicitly saved (session.Flush() being called)
On delete, the following steps are taken:
Case.Notes.Remove(item.CaseNote); //removes from the Notes collection in parent
item.CaseNote.ParentCase = null; //sets child reference to the parent to null
The expected behavior, is that the AllDeleteOrphan() will smartly remove the null-set children through a delete statement. All works fine except for one awkward scenario - when Add and Delete are called sequentially without the item being persisted - it appears the AllDeleteOrphan() does not trigger and the child foreign key id is simply set to null (causes a contraint violation, but I do not want dangling children in the database anyway).
Has anyone seen this issue before? Any ideas for potential workaround of the issue?
Any help is much appreciated - been tackling this for a while with no luck so far...

JPA with JTA how to persist many entites in one transaction

I have a list of objects. They are JPA "Location" entities.
List<Location> locations;
I have a stateless EJB which loops thru the list and persists each one.
public void createLocations() {
List<Locations> locations = getListOfJPAManagedLocationEntities(); // I'm leaving out the details of this because it has nothing to do with the issue
for(Location location : locations) {
em.persist(location);
}
}
The code works fine. I do not have any problems.
However, the issue is: I want this to be an all-or-none transaction. Currently, each time thru the for loop, the persist() method will insert a new row into the database. Suppose I have 100 location objects and the 54th object has something wrong with it and an exception is thrown. There will be 53 records inserted into the database. What I want is: all of them must succeed before any of them succeed.
I'm using the latest & greatest version of Java EE6, EJB 3.x., and JPA 2. My persistence.xml uses JTA.
<persistence-unit name="myPersistenceUnit" transaction-type="JTA">
And I like having JTA.
I do not want to stop using JTA.
90% of the time JTA does exactly what I want it to do. But in this case, I doesn't seem to.
My understanding of JTA must be inaccurate because I always thought the beginning and end of the EJB method marked the boundaries of the JTA transaction (assume only one method is in-play as I've shown above). By my logic, the transaction would not end until the for-loop is done and the method returns, and then at that point the records are persisted.
I'm using the JTDS driver for SqlServer 2008. Perhaps the database doesn't want to insert a record without immediately committing it. The entity id is defined like this:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
I've checked the spec., and it is not proper to call the various "UserTransaction" or "getTransaction()" methods in a JTA environment.
So what can I do?
Thanks.
If you use JTA and container managed transactions the default behavior for an session EJB method call is to run in a transaction (is like annotating it with #TransactionAttribute(TransactionAttributeType.REQUIRED). That means that your code already runs in a transaction and will do what you expect: if an exception occurs at row 54 all previous inserted rows will be rolled-back. You can go ahead and test it by throwing yourself an exception at some point in the loop. Note that if you throw a checked exception declared by your method you can specify what the container should do when that exception occurs. You need to annotate the exception class with #ApplicationException (rollback=true).
if there was a duplicate entry while looping then it will continue without problems and when compiler reaches this line em.flush(); after the loop then it will throw an exception and rollback the transaction.
I'm using JBoss. Set your datasource in your standalone.xml or domain.xml to have
<datasource jta="true" ...>
Seems obvious, but I obviously set it wrong a long time ago and forgot about it.

How should I do post persist/update actions in doctrine 2.1, that involves re-saving to the db?

Using doctrine 2.1 (and zend framework 1.11, not that it matters for this matter), how can I do post persist and post update actions, that involves re-saving to the db?
For example, creating a unique token based on the just generated primary key' id, or generating a thumbnail for an uploaded image (which actually doesn't require re-saving to the db, but still) ?
EDIT - let's explain, shall we ?
The above is actually a question regarding two scenarios. Both scenarios relate to the following state:
Let's say I have a User entity. When the object is flushed after it has been marked to be persisted, it'll have the normal auto-generated id of mysql - meaning running numbers normally beginning at 1, 2, 3, etc..
Each user can upload an image - which he will be able to use in the application - which will have a record in the db as well. So I have another entity called Image. Each Image entity also has an auto-generated id - same methodology as the user id.
Now - here is the scenarios:
When a user uploads an image, I want to generate a thumbnail for that image right after it is saved to the db. This should happen for every new or updated image.
Since we're trying to stay smart, I don't want the code to generate the thumbnail to be written like this:
$image = new Image();
...
$entityManager->persist($image);
$entityManager->flush();
callToFunctionThatGeneratesThumbnailOnImage($image);
but rather I want it to occur automatically on the persisting of the object (well, flush of the persisted object), like the prePersist or preUpdate methods.
Since the user uploaded an image, he get's a link to it. It will probably look something like: http://www.mysite.com/showImage?id=[IMAGEID].
This allows anyone to just change the imageid in this link, and see other user's images.
So in order to prevent such a thing, I want to generate a unique token for every image. Since it doesn't really need to be sophisticated, I thought about using the md5 value of the image id, with some salt.
But for that, I need to have the id of that image - which I'll only have after flushing the persisted object - then generate the md5, and then saving it again to the db.
Understand that the links for the images are supposed to be publicly accessible so I can't just allow an authenticated user to view them by some kind of permission rules.
You probably know already about Doctrine events. What you could do:
Use the postPersist event handler. That one occurs after the DB insert, so the auto generated ids are available.
The EventManager class can help you with this:
class MyEventListener
{
public function postPersist(LifecycleEventArgs $eventArgs)
{
// in a listener you have the entity instance and the
// EntityManager available via the event arguments
$entity = $eventArgs->getEntity();
$em = $eventArgs->getEntityManager();
if ($entity instanceof User) {
// do some stuff
}
}
}
$eventManager = $em->getEventManager():
$eventManager->addEventListener(Events::postPersist, new MyEventListener());
Be sure to check e. g. if the User already has an Image, otherwise if you call flush in the event listener, you might be caught in an endless loop.
Of course you could also make your User class aware of that image creation operation with an inline postPersist eventHandler and add #HasLifecycleCallbacks in your mapping and then always flush at the end of the request e. g. in a shutdown function, but in my opinion this kind of stuff belongs in a separate listener. YMMV.
If you need the entity id before flushing, just after creating the object, another approach is to generate the ids for the entities within your application, e. g. using uuids.
Now you can do something like:
class Entity {
public function __construct()
{
$this->id = uuid_create();
}
}
Now you have an id already set when you just do:
$e = new Entity();
And you only need to call EntityManager::flush at the end of the request
In the end, I listened to #Arms who commented on the question.
I started using a service layer for doing such things.
So now, I have a method in the service layer which creates the Image entity. After it calls the persist and flush, it calls the method that generates the thumbnail.
The Service Layer pattern is a good solution for such things.