JPA deleting inverse relationship - jpa-2.0

I have been trying to delete the inverse relationship on a JPA entity, however this have not been working well. What I'm trying right now is to set the ManyToOne property to null and then saving it using the entityManager's merge method. The ManyToOne relationship is marked with the cascade all property, however in the dataBase the foreign key is not removed. How should I do this?. Thanks a lot.

It would be easier to find out what you mean, with code in question. But I will try anyway:
#Entity
public class AEntity {
#GeneratedValue (strategy = GenerationType.SEQUENCE)
#Id int id;
//Having some cascade here doesn't matter for our case
//because we now do not cascade anything, we just set this field to
//null. Cascade=REMOVE is about never meaningful (and never fully
//fully portable) in ManyToOne side:
//just think what happens to other AEntity instances that refer to
//same BEntity.
#ManyToOne
BEntity bEntity;
public void setbEntity(BEntity bEntity) {
this.bEntity = bEntity;
}
}
public class BEntity {
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Id int id;
}
In the beginning we have following data:
AEntity(id=1,bEntity_id=2)
BEntity(id=2)
Then removing connection between a and b:
AEntity oldDirty = em.find(AEntity.class, 1);
//modify A somewhere else in code
oldDirty.setbEntity(null);
//and bring changes in:
em.merge(oldDirty);
Afterwards we have:
AEntity(id=1,bEntity_id=null)
BEntity(id=2)
If BEntity also have set that contains AEntity entities (so to say bidirectional relationship), then you have to remove A from there as well, because you have to keep care about relationship by yourself. OneToMany side is one where it can make sense to cascade removal from.

Check the cascade type of the relationship on both ends. For instance, if you want to delete all the associated entities when you delete the main entity, the annotation should look like this: #ManyToOne(cascade={CascadeType.REMOVE}), and on the inverse #OneToMany(cascade={CascadeType.REMOVE})

Related

Objectify - Order by reference

is it possible in Objectify to order by a field of a referenced object?
Example:
public class Foo {
#Id private long id;
#Index Ref<AnObject> ref;
}
public class AnObject {{
#Id private long id;
#Index name;
}
Then I try to do this query and it's not working:
Query<Foo> query = ofy().load().type(Foo.class)
.limit(Constants.DATASTORE_LIMIT)
.order("ref.name");
Thanks.
I'm afraid not. This is not something supported by the datastore. It can't do joins.
There are basically two ways around that limitation:
Denormalize and put an indexed copy of the 'name' data in your Foo object. You can do this somewhat automatically with an #OnSave method. The downside is that you need to remember to update the data in both places if it changes.
Do the work of the query planner yourself. Query for the relevant AnObjects, then query for the relevant Foos.

Doctrine2 with dynamic relationships base on a value

What I'm trying to do is basically Entity A which have field type to relate with Entity B or C base on the value of field type
for example: a Product if is type drinks to be related with DrinkOptions entity, but if it's with type food to be related with FoodOptions entity
Is there any elegant solution which can work fine in such case?
This is what I thought it will work, but it doesn't..
Changing dynamically the entity A relation will not help, because it's not yet loaded and I can't see the value of field type
combine the different option entities into one, but it will be very ugly picture with 200+ properties..
define all option types in the config and then dynamically create the mapping with the loadClassMetadata, but is not so pretty also it requires a "dummy" property, getters and setters for all option types in the Entity A
A possible solutions could be to use a single table inheritance. Might not be the nicest solution but it could fit your specific need.
<?php
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discriminator", type="string")
* #DiscriminatorMap({"drink" = "DrinkOption", "food" = "FoodOption"})
*/
class Option
{
// ...
}
/**
* #Entity
*/
class FoodOption extends Option
{
// ...
}
/**
* #Entity
*/
class DrinkOption extends Option
{
// ...
}
You then could even switch the type by altering the "discriminator" column.
I think the simpliest way would be to have multiple product entities with an inheritence
Like Product (parent) -> foodProduct (child) or DrinkProduct().
This way you could have Product link to common others entities (like price or brand) and specific link for food, drinks, smokes etc...
Product
id:
type: food/drink/smoke etc..
foodproduct: one to Many nullable: FoodProduct (it's actually one to zero/one but i find it more performant)
drinkproduct: one to Many nullable: DrinkProduct
smokeproduct:one to Many nullable: SmokeProduct
FoodProduct:
id:
product_id (many to one to Product)
options one to many to foodOptions
If it still don't fit you will have to find some design pattern

Will Doctrine2 select all fields on all associations (JOINS from a query) to populate the full aggregate object?

I'm researching whether to try Doctrine2 or not. One thing that scares me is the over SELECTing of columns I don't need (ie. consider lots of varchars being selected unnecessarily).
You might ask: but don't you want your full entity object filled? Yes, unless I'm looking for an array hydration. However, many times I don't need the full aggregation filled. Take the association shown below. If I query the Users table with a JOIN on Address, will all the columns from the address table be SELECTed as well (and therefore populated into an address object inside of users object)? Now imagine we have more JOINs. This could get really bad. What if I only want the fields from User populated in just a users only object? I guess I'm a little confused at what Doctrine is doing behind the scenes with associations and query JOINs.
/** #Entity **/
class User
{
// ...
/**
* #ManyToOne(targetEntity="Address")
* #JoinColumn(name="address_id", referencedColumnName="id")
**/
private $address;
}
/** #Entity **/
class Address
{
// ...
}
So does Doctrine2 populate all the fields of all the objects within the aggregate after a query (unless I specifiy partial)?
It depends on your query, but generally it is not implicit.
Using the query builder, you can fetch the associated record like this:
<?php
$qb = $em->createQueryBuilder();
$query = $qb->select(array("u", "a"))
->from("User", "u")
->innerJoin("u.address", "a")
->getQuery();
In the select() statement you specify what to fetch, in this case you get both.
If you only fetch the User records, then when you get the associated record with $user->getAddress(), Doctrine will make the query on the fly and hydrate the Address record for you.
That said, performance wise it is better to select both entities so Doctrine will make only one query and not 1+N queries

Restrict query results based on attributes of 1 item in 1-n association

I have an entity 'Order', and a one-to-many associated entity 'OrderStatus' (so 1 order can have many statuses). The current status of an order is defined by the last status that was added to that order.
Now I want to create a DQL query which selects all orders with a certain status. However, because this is a one-to-many relation, I have no idea how to accomplish this in DQL. I only know of querying the collection of statuses as a whole.
Does anyone have an idea if this is even possible, or do I have to use a workaround?
We had this very same problem in a project we're working on. The query you're trying to perform is very similar to this question, except that you're trying to do this in DQL, which makes it even harder. I think (but I might be wrong) that DQL does not allow this kind of query, and you may achieve the result you're expecting with a native SQL query, with all the caveats this implies.
What we ended up with, and I strongly suggest to you, is to make the current status a property of your Order. This allows easy & fast querying, with no joins required. The change is really painless:
class Order
{
/**
* #ManyToOne(targetEntity="OrderStatus")
*/
protected $status;
/**
* #OneToMany(targetEntity="OrderStatus")
*/
protected $statuses;
public function setStatus($status)
{
$orderStatus = new OrderStatus($this, $status);
$this->statuses->add($orderStatus);
$this->status = $orderStatus;
}
}
$status can also be a simple string property, if your OrderStatus is basically composed of a status string and a date; the code would then become:
class Order
{
/**
* #Column(type="string")
*/
protected $status;
// ...
public function setStatus($status)
{
// ...
$this->status = $status;
}
}

How to convert entity leaving it id

There are some entities (Region, Country, City) which used STI (or even CTI). How it possible convert Country to City leaving old id?
This is not supported in Doctrine 2 because type casting user objects is not supported by PHP.
With that said, Doctrine uses the discriminator column to determine what type of object to hydrate. If you change the value of the discriminator column in the database with a SQL UPDATE, the object type will be changed the next time the object is loaded. This works for STI, but CTI would be more complicated.
It may not be possible by standard using Doctrine, but you can work around it.
If you use the Class Metadata you can select your discriminator column.
Take a look at the Trait that I've created to solve the problem within my app:
namespace App\Doctrine\Repository;
trait DiscriminatorTrait
{
abstract public function getClassMetadata();
abstract public function getEntityManager();
private function updateDiscriminatorColumn($id, $class)
{
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new \Exception("invalid discriminator class: " . $class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
$connection = $this->getEntityManager()->getConnection();
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
}
I do have to warn you though, when your sub-classes have (a lot of) extra fields you will need to fill or clear them manually afterwards.