Differencebetween Inversedby and Mappedby

What is the difference between inversedBy and mappedBy?

  • mappedBy has to be specified on the inversed side of a (bidirectional) association
  • inversedBy has to be specified on the owning side of a (bidirectional) association

from doctrine documentation:

  • ManyToOne is always the owning side of a bidirectional assocation.
  • OneToMany is always the inverse side of a bidirectional assocation.
  • The owning side of a OneToOne assocation is the entity with the table containing the foreign key.

See https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html

Doctrine: owning side and inverse side

Take out all the things that have in your head about Owning side and Inversed side. These are nothing but some concepts that help Doctrine hydrate data into related models.

Read the following quotation from the Doctrine doc. This may somewhat help understand the concept of Owning side and Inversed side.

"Owning side" and "inverse side" are technical concepts of the ORM
technology, not concepts of your domain model. What you consider as
the owning side in your domain model can be different from what the
owning side is for Doctrine. These are unrelated.

Another quotation from the Doctrine doc:

Doctrine will only check the owning side of an association for
changes.

This means the owning side of an association is the entity with the table containing the foreign key. Therefore, the table containing foreign key will only be considered by doctrine for changes.

Once again from the Doctrine doc:

  • OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
  • The owning side of a OneToOne assocation is the entity with the table containing the foreign key

Here one instance of Company refers to one instance of the referred entity Customer or vice versa.

When we talk about OneToOne association like your example above, the owning side would be the entity with the table containing the foreign key, therefore, Customer entity.

With your example, Doctrine will create tables like the below:

CREATE TABLE Company (
id INT AUTO_INCREMENT NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;

CREATE TABLE Customer (
id INT AUTO_INCREMENT NOT NULL,
company_id INT DEFAULT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
ALTER TABLE Customer ADD FOREIGN KEY (company_id) REFERENCES Company(id);

Now if you want to get data for a customer associated with a company then the query would be:

SELECT Company.id AS CompanyID, Customer.id AS CustomerID
FROM Company
LEFT JOIN Customer ON Company.id = Customer.company.id;

The returned result from this type of query will be hydrated into both models by Doctrine.

What is the owning side and inverse side in the doctrine 2 doc example

But I don't understand which entity (group or user) is the owning side

The User entity is the owner. You have the relation of groups in User:

/**
* @ManyToMany(targetEntity="Group", inversedBy="users")
* @JoinTable(name="users_groups")
*/
private $groups;

Look above, $groups var contains the all groups associated to this user, but If you notice the property definition, $groups var has the same name of mappedBy value (mappedBy="groups"), as you did:

/**
* @ManyToMany(targetEntity="User", mappedBy="groups")
*/
private $users;

What does mappedBy mean?

This option specifies the property name on the targetEntity that is the owning side of this relation.

What is the owning side in an ORM mapping?

Why is the notion of a owning side necessary:

The idea of a owning side of a bidirectional relation comes from the fact that in relational databases there are no bidirectional relations like in the case of objects. In databases we only have unidirectional relations - foreign keys.

What is the reason for the name 'owning side'?

The owning side of the relation tracked by Hibernate is the side of the relation that owns the foreign key in the database.

What is the problem that the notion of owning side solves?

Take an example of two entities mapped without declaring a owning side:

@Entity
@Table(name="PERSONS")
public class Person {
@OneToMany
private List<IdDocument> idDocuments;
}

@Entity
@Table(name="ID_DOCUMENTS")
public class IdDocument {
@ManyToOne
private Person person;
}

From a OO point of view this mapping defines not one bi-directional relation, but two separate uni-directional relations.

The mapping would create not only tables PERSONS and ID_DOCUMENTS, but would also create a third association table PERSONS_ID_DOCUMENTS:

CREATE TABLE PERSONS_ID_DOCUMENTS
(
persons_id bigint NOT NULL,
id_documents_id bigint NOT NULL,
CONSTRAINT fk_persons FOREIGN KEY (persons_id) REFERENCES persons (id),
CONSTRAINT fk_docs FOREIGN KEY (id_documents_id) REFERENCES id_documents (id),
CONSTRAINT pk UNIQUE (id_documents_id)
)

Notice the primary key pk on ID_DOCUMENTS only. In this case Hibernate tracks both sides of the relation independently: If you add a document to relation Person.idDocuments, it inserts a record in the association table PERSON_ID_DOCUMENTS.

On the other hand, if we call idDocument.setPerson(person), we change the foreign key person_id on table ID_DOCUMENTS. Hibernate is creating two unidirectional (foreign key) relations on the database, to implement one bidirectional object relation.

How the notion of owning side solves the problem:

Many times what we want is only a foreign key on table ID_DOCUMENTS towards PERSONSand not the extra association table.

To solve this we need to configure Hibernate to stop tracking the modifications on relation Person.idDocuments. Hibernate should only track the other side of the relation IdDocument.person, and to do so we add mappedBy:

@OneToMany(mappedBy="person")
private List<IdDocument> idDocuments;

What does it mean mappedBy ?

This means something like: "modifications on this side of the relation are already Mapped By
the other side of the relation IdDocument.person, so no need to
track it here separately in an extra table."

Are there any GOTCHAs, consequences?

Using mappedBy, If we only call person.getDocuments().add(document), the foreign key in ID_DOCUMENTS will NOT be linked to the new document, because this is not the owning /tracked side of the relation!

To link the document to the new person, you need to explicitly call document.setPerson(person), because that is the owning side of the relation.

When using mappedBy, it is the responsibility of the developer to know what is the owning side, and update the correct side of the relation in order to trigger the persistence of the new relation in the database.

how to access annotation of an property(class,mappedBy,inversedBy)

The ClassMetadataInfo is what you are looking for.

Creates an instance with the entityName :

$mapping = new \Doctrine\ORM\Mapping\ClassMetadataInfo($entityNamespaceOrAlias); 

Then, get the informations you want :

Get all association names: $mapping->getAssociationNames();

Get the join column of an association:

$mapping->getSingleAssociationJoinColumnName($fieldName);

Get the mappedBy column of an association:

$mapping->getAssociationMappedByTargetField($fieldName);

...

Look at the class to know which method you can access.

Hopes it's what you expect.

EDIT

As you can access the EntityManager (i.e. from a controller), use :

$em = $this->getDoctrine()->getManager();
$metadata = $em->getClassMetadata('AppBundle:Group');

To be sure there is no problem with your entity namespace, try :

print $metadata->getTableName();

To retrieve the associations of the entity, use :

$metadata->getAssociationNames();

And to get the mapping informations of an existing association, use :

$metadata->getAssociationMapping($fieldName);

And to get all the association mappings of your entity, use:

$metadata->getAssociationMappings();

The association X refers to the inverse side field Y which is not defined as association

I'm bit confused of what you want to do.

If you want unidirectional relation from user side then remove

 * @ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="team")

this code.

And user should have only

/**
* @ORM\ManyToOne(targetEntity="App\Entity\Teams")
*/
private $teams;

On the other hand if you want bidirectional relation then add property user on teams entity.

It doesn't work for you because the mapping you defined is on $id and it should be on the property

Bidirectional way:

class Teams
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer", name="id")
*
*/
private $id;

/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="teams")
* @ORM\JoinColumn(name="team_id", referencedColumnName="id")
*/
private $user;

class User implements UserInterface, \Serializable
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;

/**
* @ORM\OneToMany(targetEntity="App\Entity\Teams", mappedBy="user")
*/
private $teams;

It's good practice to name entities with singular name so perhaps you'd like to change Teams to Team entity.

Check also http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-one-unidirectional

doctrine: refers to owning side field which does not exist (again)

Haven't tested it, but according to docs, it should look something like this

http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional

class Brand {

/**
* @var int
* @ORM\Column(name="brand_id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

...

/**
* @ORM\OneToMany(targetEntity="Device", mappedBy="brand")
*/
private $devices;
}

and

class Device {
/**
* @var int
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

...

/**
* @ORM\ManyToOne(targetEntity="Brand", inversedBy="devices")
* @ORM\JoinColumn(name="brand_id", referencedColumnName="id", nullable=true)
*/
private $brand;
}

Can someone explain mappedBy in JPA and Hibernate?

By specifying the @JoinColumn on both models you don't have a two way relationship. You have two one way relationships, and a very confusing mapping of it at that. You're telling both models that they "own" the IDAIRLINE column. Really only one of them actually should! The 'normal' thing is to take the @JoinColumn off of the @OneToMany side entirely, and instead add mappedBy to the @OneToMany.

@OneToMany(cascade = CascadeType.ALL, mappedBy="airline")
public Set<AirlineFlight> getAirlineFlights() {
return airlineFlights;
}

That tells Hibernate "Go look over on the bean property named 'airline' on the thing I have a collection of to find the configuration."

problem understanding relation mapping in doctrine 2

The mappedBy attribute does say nothing about the name of the foreign key, That is what the "@JoinColumn" annotation is for. The correct mapping for this scenario would be:

/**
* @Entity
* @Table(name="comments")
**/
class Comments
{
/** @Id @Column(type="integer") */
private $id;
/** @Column(type="text") */
private $text;

/**
* @OneToMany(targetEntity="keywords", mappedBy="comment")
*/
private $keywords;

public function getText(){return $this->text;}
public function getId(){return $this->id;}
public function getKeywords(){return $this->keywords;}
}

/**
* @Entity
* @Table(name="keywords")
*/
class Keywords
{
/** @Id @Column(type="integer") */
private $id;

/**
* @ManyToOne(targetEntity="Comments", inversedBy="keywords")
*/
private $comment;

/**
* @Column(type="text") */
private $text;

public function getText(){return $this->text;}
public function getId(){return $this->id;}
}

Using Schema Tool it generates the SQL which equals your schema:

CREATE TABLE comments (id INT NOT NULL, text LONGTEXT NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE keywords (id INT NOT NULL, comment_id INT DEFAULT NULL, text LONGTEXT NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
ALTER TABLE keywords ADD FOREIGN KEY (comment_id) REFERENCES comments(id);

Two problems in your mapping:

  1. You have to understand the difference between owning and inverse side. Just having a One-To-Many uni-directional relation requires a third join table. However with a bi-directional relation like in my mapping with the owning side property Keyword::$comment it works.
  2. "mappedBy" refers to the property on the other targetEntity that is "the other side of this associations". InversedBy has somewhat the same meaning, just that inversedBy is always specified on the owning side, mappedBy on the inverse side.

This all sounds very complicated, but its a very efficient way from the ORMs technical perspective, because it allows to update associations with the least number of required SQL UPDATE statements. See the docs on how Inverse/Owning works:

http://www.doctrine-project.org/projects/orm/2.0/docs/reference/association-mapping/en#owning-side-and-inverse-side



Related Topics



Leave a reply



Submit