JPA Self join error - jpa-2.0

I have an error with my entities, my tables are:
LANGUAGE
codLanguage (Primary Key)
nameLanguage
Device
idDevice(Primary Key) nameDevice
PHRASE
idPhraseGroup(Primary Key) codlanguage(Primary Key) idDevice
(Primary Key) tex
I have problem with my entity Phrase,it is :
public class Phrase implements Serializable {
#EmbeddedId
private PhraseKey idPhrase;
private String text;
//etc
//here my problem (*)
#OneToMany(mappedBy="idPhrase.idPhraseGroup",fetch=FetchType.EAGER)
#JoinColumn(name = "idPhrase.idPhraseGroup", updatable = false, insertable = false, referencedColumnName = "idPhrase.idPhraseGroup")
private List<Phrase> groupListPhrase;
}
#Embeddable
public class PhraseKey implements Serializable {
private Integer idPhraseGroup;
private String codLanguage;
private String idDevice;
---getter e setter
}
I would you like to get a list of phrases with the same idPhraseGroup
for example in Phrase table :
idPhraseGroup | codLang |idDevice | text
1 | ES | 1 | mesa
1 | EN | 1 | table
..but i've got this error:
Exception Description: An incompatible mapping has been encountered
This usually occurs when the cardinality of a mapping does not
correspond with the cardinality of its backpointer
Thanxs

I do not see why you need the mapping, or how it can work. You cannot use the basic mapping "idPhrase.idPhraseGroup" field in the mappedBy on a oneToMany because it does not describe a relationship. A oneToMany generally relies on the other side having a manyToOne back, but in this case you dont.
If all you want is a collection of Phrase entities with a particular idPhaseGroup, just query for it using:
em.createQuery("select p from Phase p where p.idPhrase.idPhraseGroup = :phaseGroup").setParameter("phaseGroup", phaseGroupId);
I would remove the mapping unless you really need it cached within the Phaze entity. If you do, it would be better mapped like:
#OneToMany
#JoinColumn(name="IDPHASEGROUP", referencedColumnName="IDPHASEGROUP", insertable=false, updatable=false)
private List groupListPhrase;

Related

JPA - Unidirectional relationship containing List with duplicate entries

I have two entities, TestCase and TestCaseStep, defined as follows:
TestCase
#Entity
#Table(name = "TEST_CASE")
public class TestCase implements Serializable, TestCase
{
#Id
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "Testcase_Step_Association", joinColumns = { #JoinColumn(name = "TC_NAME", referencedColumnName = "NAME") }, inverseJoinColumns = {
#JoinColumn(name = "STEP_NAME", referencedColumnName = "NAME") })
#OrderColumn(name = "STEP_NUMBER", nullable = false)
private List<TestCaseStep> testCaseSteps;
[...]
}
TestCaseStep
#Entity
#Table(name = "TEST_CASE_STEPS")
public class TestCaseStep implements Serializable, TestCaseStep
{
#Id
#Column(name = "name")
private String name;
[...]
}
This works fine as long as I do not try to insert the same object into the list of test case steps more than once. As soon as I try that, I get a primary key violation:
Caused by: org.h2.jdbc.JdbcSQLException: Eindeutiger Index oder Prim�rschl�ssel verletzt: "PRIMARY_KEY_9 ON PUBLIC.TESTCASE_STEP_ASSOCIATION(TC_NAME, TESTCASESTEPS_NAME) VALUES ('TESTCASE_1', 'OUT_STEP', 395)"
Unique index or primary key violation: "PRIMARY_KEY_9 ON PUBLIC.TESTCASE_STEP_ASSOCIATION(TC_NAME, TESTCASESTEPS_NAME) VALUES ('TESTCASE_1', 'OUT_STEP', 395)"; SQL statement:
INSERT INTO Testcase_Step_Association (testCaseSteps_NAME, TC_NAME, STEP_NUMBER) VALUES (?, ?, ?) [23505-191]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.index.BaseIndex.getDuplicateKeyException(BaseIndex.java:107)
at org.h2.mvstore.db.MVSecondaryIndex.checkUnique(MVSecondaryIndex.java:230)
at org.h2.mvstore.db.MVSecondaryIndex.add(MVSecondaryIndex.java:189)
at org.h2.mvstore.db.MVTable.addRow(MVTable.java:704)
at org.h2.command.dml.Insert.insertRows(Insert.java:156)
at org.h2.command.dml.Insert.update(Insert.java:114)
at org.h2.command.CommandContainer.update(CommandContainer.java:98)
at org.h2.command.Command.executeUpdate(Command.java:258)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:160)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:146)
at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:892)
... 193 more
I need to allow my users to repeat the same test case step multiple times. How can I accomplish this without using a separate entity to map the relationship by myself?
Instead of defining name as a primary key in both TestCase and TestCaseSteps Entity,try to define a Surrogate Key as a primary key wherein you keep the Generation Strategy of that Surrogate Key to AUTO
In this way ,you always make a new Insert when you are rerunning test cases.

Inheritance & Association

I need media entities defined like this:
Category:
name : string (25)
slug : string (25)
description: text
documents : associaion with Document
Document = #ORM\InheritanceType("SINGLE_TABLE"):
category : association (Category) and discriminator
title : string (50)
description: text
slug : string (50)
For example, categories:
VIDEO:
youtube
dailymotion
vimeo
PHOTO:
locale photo
cloud photo
FILE:
locale file
cloud file
A document of a category would be an instance of Document\Youtube etc ...
Is it possible to use a column as association field and discriminator for inheritance ?
The official documentation doesn't seem to mention a restriction about this.
However, as the column you want is a relation, the discriminator map would then rely on DB-issued IDs, which is not good in my opinion...
But you still can let Doctrine handle the discriminator for you: just declare a discriminator column you'll never use directly, declare its map, then rely on the entity logic to keep coherence between the category and the document type:
/**
* #ORM\Entity(repositoryClass="...
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({
* "youtube"="Youtube",
* "dailymotion"="Dailymotion",
* ...
* })
*/
abstract class Document {
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="documents")
*/
protected $category;
...
//this is to be called by children that all share a category field
public function setCategory(Category $c) {
$class = get_class($this);
if (strtolower($c->getName()) !== strtolower(substr($class, strrpos($class, '\\') + 1))) {
throw new \LogicException("Cannot bind " . $class . " to category " . $c->getName());
}
$this->category = $c;
return $this;
}

how works createQueryBuilder and leftJoin?

I dont get how to make it work.
I have:
a table partner with fields id and name
a table partner_address with two fields: id_partner and id_address
a table address with fields id and external key id_town which references town(id)
a table town with fields id, a name, and postal_code
I want to select all partners that are in towns with specific postal_code
This query works:
SELECT p.nom, v.nom
FROM partner p
JOIN partner_address pa
ON pa.id_partner=p.id
JOIN address a
ON pa.id_address = a.id
JOIN town t
ON a.id_town=t.id
WHERE t.postal_code='13480';
Now I want to "translate" it into Doctrine 2 full syntax, following the documentation.
So I've made a custom repository:
src/Society/Bundle/MyProjectBundle/Repository/PartnerRepository.php
In this repository, I'm trying to create the corresponding function:
<?php
namespace HQF\Bundle\PizzasBundle\Repository;
use Doctrine\ORM\EntityRepository;
class PartenaireRepository extends EntityRepository
{
/**
* Get all active partners from a given postal code.
*/
public function findAllActiveByCp($cp)
{
return $this->createQueryBuilder('p')
->where('p.dateVFin IS NULL')
->andWhere('p.cp=:cp')
->addOrderBy('p.cp', 'DESC')
->setParameter('cp', $cp);
}
}
Nota: the query in the code is not the right one but this code works in another custom repository I've made, so I'm trying to start from this code.
I'm trying something like this but it doesn't work:
public function findAllActiveByCp($cp)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftJoin('partner_address pa ON pa.id_partner=p.id')
->leftJoin('address a ON pa.id_address = a.id')
->leftJoin('town t ON a.id_ville=t.id')
->where('p.dateVFin IS NULL')
->andWhere('t.cp=:cp')
->addOrderBy('t.cp', 'DESC')
->setParameter('cp', $cp);
}
I get this error:
Warning: Missing argument 2 for Doctrine\ORM\QueryBuilder::leftJoin(),
called in
/blabla/Repository/PartenaireRepository.php
on line 18 and defined in
/blabla/symfony/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php
line 767
You have to join only properties, that the selected entity have.
In first parameter of join() or leftJoin() or xxxJoin() you pass the attribute name related to selected object, and in the second - alias for joined entity.
Try similar to this:
$q = $this->em()->createQueryBuilder();
$q->select(['item', 'itemContact'])
->from('ModuleAdmin\Entity\CustomerEntity', 'item')
->leftJoin('item.contacts', 'itemContact')
->andWhere($q->expr()->like('item.name', ':customerNameStart'));
Of course, the CustomerEntity contains OneToMany relation in field contacts.
Remember, that in select statement you have to select the root entity (in my example CustomerEntity aliased as item).
Edit by Olivier Pons to add how I found out the solution, and to mark this answer as valid, because it put me on the right track, thank you Adam!
In the file PartenaireRepository.php I've used the createQueryBuilder('p') properly. Here's how to make two joins in a row, using createQueryBuilder():
class PartenaireRepository extends EntityRepository
{
/**
* Retrieval of all partners given for a given postal code.
*/
public function findAllActiveByCp($cp)
{
return $this->createQueryBuilder('p')
->leftJoin('p.adresses', 'a')
->leftJoin('a.ville', 'v')
->where('v.cp=:cp')
->setParameter('cp', $cp);
... blabla
}
}
I believe for what you're doing, you will need to provide four arguments to the leftJoin method.
->leftJoin('partner_address', 'pa', 'ON', 'pa.id_partner = p.id')
So your query builder chain should look like this
public function findAllActiveByCp($cp)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftJoin('partner_address', 'pa', 'ON', 'pa.id_partner = p.id')
->leftJoin('address', 'a', 'ON', 'pa.id_address = a.id')
->leftJoin('town', 't', 'ON', 'a.id_ville = t.id')
->where('p.dateVFin IS NULL')
->andWhere('t.cp=:cp')
->addOrderBy('t.cp', 'DESC')
->setParameter('cp', $cp)
;
}

How to get the first member of the related collection in JPQL

I have Product table which has a related table Images with a relation 1:M.
Class Product {
private Integer productId;
private String productName;
....
....
....
private List<Image> productImageList;
....
....
....
}
Class Image{
private Integer imageId;
private String imageName;
}
Class ProductLite{
private Integer productId;
private String productName;
private String imageName;
}
I am trying a JPQL query where I want to query to fetch products and the first image from the productImageList and returning a ProductLite object using the new constructor.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<ProductLite> getAllProductLite() {
Query q = em.createQuery("SELECT NEW com.mycomp.application.entity.ProductLite(p.productId, p.productName, p.productImageList.get(0).getImageName())"
+ " from Product p"
+ " ORDER by p.productName");
List<ProductLite> prods = q.getResultList();
return prods;
}
But for some reason I am not able to get it to work. I get a NoViableException. So I tried moving the logic of getting the first image (getImage() method) to the Product Entity so in the query I could just call the getImage(). Even that does not seem to work.
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [SELECT NEW com.meera.application.entity.ProductLite(distinct p.productId, p.productName, p.getImage()) from Product p, IN(p.productImageList) pil where p.category.categoryCode = :categoryCode ORDER by p.productName ], line 1, column 52: unexpected token [distinct].
Internal Exception: NoViableAltException(23#[452:1: constructorItem returns [Object node] : (n= scalarExpression | n= aggregateExpression );])
Any help is appreciated.
First, you cannot call methods in entity class from your JP QL query. Second, to use the order of entities in list, you need persisted order.
To create column for order to the join table between image and product, you have to add
#OrderColumn-annotation to the productImageList. For example:
#OrderColumn(name = "myimage_order")
//or dont't define name and let it default to productImageList_order
#OneToMany
private List<Image> productImageList;
Then you have to modify query to use that order to choose only first image:
SELECT NEW com.mycomp.application.entity.ProductLite(
p.productId, p.productName, pil.imageName)
FROM Product p JOIN p.productImageList pil
WHERE INDEX(pil) = 0
ORDER by p.productName

EF4: Get the linked column names from NavigationProperty of an EDMX

I am generating POCOs (lets say they are subclasses of MyEntityObject) by using a T4 template from an EDMX file.
I have 3 entities, e.g.:
MyTable1 (PrimaryKey: MyTable1ID)
MyTable2 (PrimaryKey: MyTable2ID)
MyTable3 (PrimaryKey: MyTable3ID)
These entities have the following relations:
MyTable1.MyTable1ID <=>
MyTable2.MyTable1ID (MyTable1ID is the
foreign key to MyTable1)
MyTable2.MyTable2ID <=>
MyTable3.MyTable2ID (MyTable2ID is the
foreign key to MyTable2)
Or in another view:
MyTable1 <= MyTable2 <= MyTable3
I want to extract all foreign key relations
NavigationProperty[] foreignKeys = entity.NavigationProperties.Where(np => np.DeclaringType == entity && ((AssociationType)np.RelationshipType).IsForeignKey).ToArray();
forewach (NavigationProperty foreignKey in foreignKeys)
{
// generate code....
}
My Question: How can I extract the column names that are linked between two entities?
Something like this:
void GetLinkedColumns(MyEntityObject table1, MyEntityObject table2, out string fkColumnTable1, out string fkColumnTable2)
{
// do the job
}
In the example
string myTable1Column;
string myTable2Column;
GetLinkedColumns(myTable1, myTable2, out myTable1Column, out myTable2Column);
the result should be
myTable1Column = "MyTable1ID";
myTable2Column = "MyTable2ID";
The first answer works if your foreign key columns are exposed as properties in your conceptual model. Also, the GetSourceSchemaTypes() method is only available in some of the text templates included with EF, so it is helpful to know what this method does.
If you want to always know the column names, you will need to load the AssociationType from the storage model as follows:
// Obtain a reference to the navigation property you are interested in
var navProp = GetNavigationProperty();
// Load the metadata workspace
MetadataWorkspace metadataWorkspace = null;
bool allMetadataLoaded =loader.TryLoadAllMetadata(inputFile, out metadataWorkspace);
// Get the association type from the storage model
var association = metadataWorkspace
.GetItems<AssociationType>(DataSpace.SSpace)
.Single(a => a.Name == navProp.RelationshipType.Name)
// Then look at the referential constraints
var toColumns = String.Join(",",
association.ReferentialConstraints.SelectMany(rc => rc.ToProperties));
var fromColumns = String.Join(",",
association.ReferentialConstraints.SelectMany(rc => rc.FromProperties));
In this case, loader is a MetadataLoader defined in EF.Utility.CS.ttinclude and inputFile is a standard string variable specifying the name of the .edmx file. These should already be declared in your text template.
Not sure exactly whether you want to generate code using the columns or not, but this may partly help to answer your question (How can I extract the column names that are linked between two entities?) ...
NavigationProperty[] foreignKeys = entity.NavigationProperties
.Where(np => np.DeclaringType == entity &&
((AssociationType)np.RelationshipType).IsForeignKey).ToArray();
foreach (NavigationProperty foreignKey in foreignKeys)
{
foreach(var rc in GetSourceSchemaTypes<AssociationType>()
.Single(x => x.Name == foreignKey.RelationshipType.Name)
.ReferentialConstraints)
{
foreach(var tp in rc.ToProperties)
WriteLine(tp.Name);
foreach(var fp in rc.FromProperties)
WriteLine(fp.Name);
}
}
This code works fine on my Visual Studio 2012
<## template language="C#" debug="true" hostspecific="true"#>
<## include file="EF.Utility.CS.ttinclude"#>
<#
string inputFile = #"DomainModel.edmx";
MetadataLoader loader = new MetadataLoader(this);
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{
foreach (NavigationProperty navProperty in entity.NavigationProperties)
{
AssociationType association = ItemCollection.GetItems<AssociationType>().Single(a => a.Name == navProperty.RelationshipType.Name);
string fromEntity = association.ReferentialConstraints[0].FromRole.Name;
string fromEntityField = association.ReferentialConstraints[0].FromProperties[0].Name;
string toEntity = association.ReferentialConstraints[0].ToRole.Name;
string toEntityField = association.ReferentialConstraints[0].ToProperties[0].Name;
}
}
#>