I created a CRUD which manages events, another other CRUD which manages styles of music.
I made a manytomany relationship between the two, and I want to choose from my event creation form my style of music.
So I made a formtype for the events that I use for the creation and for the edition.
The crud worked very well with me doing my relationship. The creation of event still works but I have this error when I want to access the view of the modification form of my event :
Unable to transform value for property path "music_type": Expected a Doctrine\Common\Collections\Collection object.
what do you think ?
part music style for my eventtype :
->add('music_type', EntityType::class, [
'class' => MusicStyle::class,
'query_builder' => function (MusicStyleRepository $r) {
return $r->createQueryBuilder('i')
->orderBy('i.name', 'ASC');
},
'label' => 'Style de musique',
'label_attr' => [
'class' => 'form-label mt-4'
],
'choice_label' => 'name',
'multiple' => true,
'expanded' => true,
])
My entity Event :
* #return Collection<int, MusicStyle>
*/
public function getMusicStyles(): Collection
{
return $this->musicStyles;
}
public function addMusicStyle(MusicStyle $musicStyle): self
{
if (!$this->musicStyles->contains($musicStyle)) {
$this->musicStyles[] = $musicStyle;
$musicStyle->addEvent($this);
}
return $this;
}
public function removeMusicStyle(MusicStyle $musicStyle): self
{
if ($this->musicStyles->removeElement($musicStyle)) {
$musicStyle->removeEvent($this);
}
return $this;
}
My eventController
public function edit(
EventRepository $repository,
Event $event,
Request $request,
EntityManagerInterface $manager
): Response {
$form = $this->createForm(EventType::class, $event);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$event = $form->getData();
$manager->persist($event);
$manager->flush();
$this->addFlash(
'success',
'Ton event a bien été modifié !'
);
return $this->redirectToRoute('event.index');
}
return $this->render('pages/event/edit.html.twig', [
'form' => $form->createView()
]);
}
my migration for the event table
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE event (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT NOT NULL, picture VARCHAR(255) NOT NULL, city VARCHAR(255) NOT NULL, adress VARCHAR(255) NOT NULL, place_name VARCHAR(255) DEFAULT NULL, date DATETIME NOT NULL, music_type VARCHAR(255) NOT NULL, event_type VARCHAR(255) NOT NULL, participants INT DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
my migration for the ManyToMany
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE music_style (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE music_style_event (music_style_id INT NOT NULL, event_id INT NOT NULL, INDEX IDX_5F0B2D4F7DDE3C52 (music_style_id), INDEX IDX_5F0B2D4F71F7E88B (event_id), PRIMARY KEY(music_style_id, event_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE music_style_event ADD CONSTRAINT FK_5F0B2D4F7DDE3C52 FOREIGN KEY (music_style_id) REFERENCES music_style (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE music_style_event ADD CONSTRAINT FK_5F0B2D4F71F7E88B FOREIGN KEY (event_id) REFERENCES event (id) ON DELETE CASCADE');
}
this may seem like a silly question so i'm novice in symfony
Try changing the property type from Collection to ArrayCollection instead. Because the querybuilder returns an array, not a Collection.
Another thing to check is, if you are initializing the property in your entity constructor, like
public function __construct()
{
$this->musicStyles = new ArrayCollection();
}
Related
I can insert table column names dynamically the problem is when I want to insert the new or old values in my log table I am getting a string 'old.Colname' or 'new.Colname' instead of old or new value.
DECLARE C_USER VARCHAR(255);
DECLARE VARIABLE OPERATION_EVENT CHAR(8);
DECLARE GROUPIDNO INT;
DECLARE LOGDATAID_NO INT;
DECLARE VARIABLE FN CHAR(31);
DECLARE VARIABLE NEWCOL CHAR(31);
DECLARE VARIABLE OLDCOL CHAR(31);
BEGIN
SELECT CURRENT_USER FROM RDB$DATABASE INTO :C_USER;
IF (DELETING) THEN
BEGIN
OPERATION_EVENT = 'DELETE';
END
ELSE
BEGIN
IF (UPDATING) THEN
OPERATION_EVENT = 'UPDATE';
ELSE
OPERATION_EVENT = 'INSERT';
END
SELECT MAX(GROUPID) FROM LOG_DATA INTO :GROUPIDNO;
IF(GROUPIDNO IS NULL) THEN
GROUPIDNO = 1;
ELSE
GROUPIDNO = GROUPIDNO + 1;
IF(INSERTING) THEN
BEGIN
FOR SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS WHERE RDB$RELATION_NAME = 'ARAC' INTO :FN DO
BEGIN
OLDCOL = 'old.'||:FN;
NEWCOL = 'new.'||:FN;
INSERT INTO LOG_DATA (OLD_VALUE,NEW_VALUE, COLUMN_NAME, TABLE_NAME, OPERATION,
CREATEDAT,USERS,GROUPID,LOGDATAID)
VALUES (:OLDCOL,:NEWCOL,:FN,'ARAC',trim(:OPERATION_EVENT),
current_timestamp,:C_USER,:GROUPIDNO,:LOGDATAID_NO + 1);
END
END
Here is a screen shot of my log table, I want to insert the old and new values, but column names are being inserted as strings instead
The problem is that you are trying to reference the old and new context as strings, and that is not possible. The specific problem is:
OLDCOL = 'old.'||:FN;
NEWCOL = 'new.'||:FN;
This produces a string with value 'old.<whatever the value of FN is>' (and same for new). It does not produce the value of the column with the name in FN from the OLD or NEW contexts.
Unfortunately, it is not possible to dynamically reference the columns in the OLD and NEW contexts by name. You will explicitly need to use OLD.columnname and NEW.columnname in your code, which means that you will need to write (or generate) a trigger that inserts each column individually.
Alternatively, you could upgrade to Firebird 3, and use a UDR to define a trigger in native code, C# or Java (or other supported languages). These UDR engines allow you to dynamically reference the columns in the old and new context.
As an example, using the FB/Java external engine (check the readme in the repository on how to install FB/Java):
Create a CHANGELOG table:
create table changelog (
id bigint generated by default as identity constraint pk_changelog primary key,
tablename varchar(31) character set unicode_fss not null,
row_id varchar(30) character set utf8,
columnname varchar(31) character set unicode_fss not null,
new_value varchar(2000) character set utf8,
old_value varchar(2000) character set utf8,
operation char(6) character set ascii not null,
modification_datetime timestamp default localtimestamp not null
)
And a FB/Java trigger:
package nl.lawinegevaar.fbjava.experiment;
import org.firebirdsql.fbjava.TriggerContext;
import org.firebirdsql.fbjava.Values;
import org.firebirdsql.fbjava.ValuesMetadata;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiPredicate;
import static java.util.Collections.unmodifiableSet;
import static org.firebirdsql.fbjava.TriggerContext.Action.*;
public class ChangelogTrigger {
private static final Set<TriggerContext.Action> SUPPORTED_ACTIONS =
unmodifiableSet(EnumSet.of(INSERT, UPDATE, DELETE));
private static final int ROW_ID_LENGTH = 30;
private static final int VALUE_LENGTH = 2000;
private static final String INSERT_CHANGELOG =
"insert into changelog (tablename, row_id, columnname, new_value, old_value, operation) "
+ "values (?, ?, ?, ?, ?, ?)";
public static void logChanges() throws SQLException {
TriggerContext ctx = TriggerContext.get();
TriggerContext.Action action = ctx.getAction();
if (!SUPPORTED_ACTIONS.contains(action)) {
return;
}
String tableName = ctx.getTableName();
if (tableName.equals("CHANGELOG")) {
throw new IllegalStateException("Cannot apply ChangelogTrigger to table " + tableName);
}
String identifierColumn = ctx.getNameInfo();
ValuesMetadata fieldsMetadata = ctx.getFieldsMetadata();
int identifierIdx = identifierColumn != null ? fieldsMetadata.getIndex(identifierColumn) : -1;
Values oldValues = ctx.getOldValues();
Values newValues = ctx.getNewValues();
Values primaryValueSet = action == INSERT ? newValues : oldValues;
String identifierValue = identifierIdx != -1
? truncate(asString(primaryValueSet.getObject(identifierIdx)), ROW_ID_LENGTH)
: null;
try (PreparedStatement pstmt = ctx.getConnection().prepareStatement(INSERT_CHANGELOG)) {
pstmt.setString(1, tableName);
pstmt.setString(2, identifierValue);
BiPredicate<Object, Object> logColumn = action == UPDATE
? ChangelogTrigger::acceptIfModified
: ChangelogTrigger::acceptAlways;
boolean batchUsed = false;
for (int idx = 1; idx <= fieldsMetadata.getParameterCount(); idx++) {
Object oldValue = oldValues != null ? oldValues.getObject(idx) : null;
Object newValue = newValues != null ? newValues.getObject(idx) : null;
if (logColumn.test(oldValue, newValue)) {
String columnName = fieldsMetadata.getName(idx);
pstmt.setString(3, columnName);
pstmt.setString(4, truncate(asString(newValue), VALUE_LENGTH));
pstmt.setString(5, truncate(asString(oldValue), VALUE_LENGTH));
pstmt.setString(6, action.name());
pstmt.addBatch();
batchUsed = true;
}
}
if (batchUsed) {
pstmt.executeBatch();
}
}
}
private static boolean acceptAlways(Object oldValue, Object newValue) {
return true;
}
private static boolean acceptIfModified(Object oldValue, Object newValue) {
return !Objects.equals(oldValue, newValue);
}
private static String asString(Object value) {
return value != null ? String.valueOf(value) : null;
}
private static String truncate(String value, int maxLength) {
if (value == null || value.length() <= maxLength) {
return value;
}
return value.substring(0, maxLength - 3) + "...";
}
}
This FB/Java trigger is very generic and can be used for multiple tables. I haven't tested this trigger with all datatypes. For example, to be able to make the trigger work correctly with columns of type blob or other binary types will require additional work.
Build the trigger and load it into the database using the fbjava-deployer utility of FB/Java.
Then define the trigger on the table you want (in this case, I defined it on the TEST_CHANGELOG table):
create trigger log_test_changelog
before insert or update or delete
on test_changelog
external name 'nl.lawinegevaar.fbjava.experiment.ChangelogTrigger.logChanges()!ID'
engine JAVA
The external name defines the routine to call (nl.lawinegevaar.fbjava.experiment.ChangelogTrigger.logChanges()) and the name of the (single) primary key column of the table (ID), which is used to log the identifier in the ROW_ID column.
I am completely new here.Could anyone please give me some help?My Syntax is as following.
PO:
package com.cabr.po;
public class InputVAT {
private String dept;
private String period;
private String tax;
private String type;
}
toString and get,set methods are ommited here.
Aliases in configuration:
<typeAliases>
<package name="com.cabr.po" />
</typeAliases>
DaoImplement:
#Override
public InputVAT findInputVATByPeriod(String period) {
SqlSession sqlSession = sqlSessionFactory.openSession();
InputVAT inputVAT = sqlSession.selectOne("com.cabr.findInputVATByPeriod", period);
return inputVAT;
}
mapper:
<mapper namespace="com.cabr">
<select id="findInputVATByPeriod" parameterType="string"
resultType="InputVAT">
SELECT * FROM input_vat WHERE period = #{period}
</select>
test:
#Test
public void testFindInputVATByPeriod() {
InputVATDao dao = new InputVATDaoImpl(sqlSessionFactory);
InputVAT inputVAT = dao.findInputVATByPeriod("201607");
System.out.println(inputVAT);
}
database:
CREATE TABLE `input_vat` (
`id` varchar(32) NOT NULL,
`dept` varchar(10) NOT NULL,
`period` varchar(6) NOT NULL,
`tax` varchar(10) NOT NULL,
`type` varchar(128) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
When I try to run this test,the console shows:
2016-08-22 15:54:08,282 [main] [com.cabr.findInputVATByPeriod]-[DEBUG] ==>
Preparing: SELECT * FROM input_vat WHERE period = ?
2016-08-22 15:54:08,360 [main] [com.cabr.findInputVATByPeriod]-[DEBUG] ==>
Parameters: 201607(String)
2016-08-22 15:54:08,462 [main] [com.cabr.findInputVATByPeriod]-[DEBUG] <==
Total: 0
null
I wish I have descripted my problem clearly.I wonder why the result is null while there is actually data in the database.
Well...I found some answers noticing that this may work:
<select id="findInputVATByPeriod" parameterType="string"
resultMap="MyresultMap">
SELECT * FROM input_vat WHERE period = #{period}
</select>
<ResultMap id="MyresultMap" type="InputVAT">
<id column="id" property="id">
</ResultMap>
In fact I didn't save the data in my database correctly.What a shame!
i am a little baffled by this;
my post forms is not populating the values received from the returned post values; i suspect the problem is arising from my getJobId() in my jobsort class values;
below is my form:
public function jobSortAction()
{
$form = new CreateJobSortForm($this->getEntityManager());
$jobSort = new JobSort();
$form->setInputFilter($jobSort->getInputFilter());
$id= 11;
$jobSort->setId($id);
$form->bind($jobSort);
if ($this->request->isPost()) {
//$post = $this->request->getPost();
$form->setData($this->request->getPost());
//var_dump($post);
//var_dump($jobSort);
if ($form->isValid()) {
$this->getEntityManager()->persist($jobSort);
$this->getEntityManager()->flush();
}
}
return array('form' => $form);
}
below is the var_dumped values of the 'return post values' and the Jobsort() object. You will note that the returned post values has values for both the Id and the JobId
object(Zend\Stdlib\Parameters)[168]
public 'JobSort' =>
array (size=2)
'jobId' => string '5' (length=1)
'id' => string '11' (length=2)
public 'submit' => string 'Submit' (length=6)
object(Workers\Entity\JobSort)[394]
protected 'inputFilter' => null
protected 'id' => int 11
protected 'jobId' => null
protected 'workerservicelist' => null
yet, when i populate the values, it does not seem to record the values for the jobId
below is my jobsort entity class:
class JobSort
{
protected $inputFilter;
/**
* #ORM\Id
*
* #ORM\Column(name="user_id", type="integer")
*/
protected $id;
/**
* #ORM\Column(name="jobId", type="integer")
*/
protected $jobId;
public function setId($id)
{
return $this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setJobId($jobId)
{
return $this->jobId = $jobId;
}
public function getJobId( )
{
return $this->jobId;
}
is there any advice or suggestions on what i need to do to find out why the values are not been populated
warm regards
Andreea
by the way; the form actually works when i had the Id of CLASS jobsort set to
#ORM\GeneratedValue(strategy="AUTO")
the problem started when i took it out and set it to manual
Hello again
here is my form:
this is the error message i received;
An exception occurred while executing 'INSERT INTO worker_main_jobsort (user_id, jobId) VALUES (?, ?)' with params [11, null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'jobId' cannot be null
here is my form:
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
use Workers\Form\Fieldset\JobSortFieldset;
class CreateJobSortForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('create-Job-post-form');
// The form will hydrate an object of type "BlogPost"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\JobSort'));
// Add the user fieldset, and set it as the base fieldset
$JobSortFieldset = new JobSortFieldset($objectManager);
$JobSortFieldset->setUseAsBaseFieldset(true);
$this->add($JobSortFieldset);
// Optionally set your validation group here
// … add CSRF and submit elements …
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Submit',
'id' => 'submitbutton',
),
));
// Optionally set your validation group here
}
}
and here is the fieldset class:
class JobSortFieldset extends Fieldset
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('JobSort');
$id= 10;
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\JobSort'))
->setObject(new JobSort());
}
}
this addition is in response to rafaame solution;
i amended my form as recommended; however it still not working. i think the issue now is that Rafaame solution is in regarding to zendDB save method, but i am using doctrine persis**t and **flush method . i accordingly get the following error message;
Call to undefined method Workers\Entity\JobSort::save()
below is my amended form:
public function jobSortAction()
{
$form = new CreateJobSortForm($this->getEntityManager() );
$jobSort = new JobSort();
if($this->request->isPost())
{
$form->setData($this->request->getPost());
if ($form->isValid())
{
$entity = $form->getData();
$model = new JobSort();
$model->save($entity);
// $this->getEntityManager()->persist( $model);
// $this->getEntityManager()->flush();
}
}
return array('form' => $form);
}
in response to Rafaame question about what problems i had,the message that i am now receiving is this:
**
EntityManager#persist() expects parameter 1 to be an entity object,
array given.
**
below is my function:
public function jobSortAction()
{
$serviceLocator = $this->getServiceLocator();
$objectManager = $this->getEntityManager();
$form = new CreateJobSortForm($this->getEntityManager());
if ($this->request->isPost())
{
$form->setData($this->request->getPost());
if ($form->isValid()) {
$entity = $form->getData();
$model = new JobSort($objectManager, $serviceLocator);
$model->getEntityManager()->persist($entity);
$model->getEntityManager()->flush();
}
}
return array('form' => $form);
}
my form; i.e where the hydrator should be set
namespace Workers\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
use Workers\Form\Fieldset\JobSortFieldset;
class CreateJobSortForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('JobSort');
// The form will hydrate an object of type "BlogPost"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\JobSort'));
// Add the user fieldset, and set it as the base fieldset
$JobSortFieldset = new JobSortFieldset($objectManager);
$JobSortFieldset->setUseAsBaseFieldset(true);
$this->add($JobSortFieldset);
If you check your code, you are creating a JobSort entity, setting only its id and binding it to the form:
$jobSort = new JobSort();
$jobSort->setId($id);
$form->bind($jobSort);
After that, you are dumping $jobSort and $this->request->getPost(). So, obviously, you are getting jobId in the POST data but not in the entity (you didn't set the entity's jobId before binding it to the form). There's nothing wrong with your entity's code.
The solution for this: don't bind anything to the form. You should only bind an entity to the form in the case of an edit action, that you fetch the entity from the database and want to populate the form with its values.
Example of add action:
public function addAction()
{
$serviceLocator = $this->getServiceLocator();
$objectManager = $this->getObjectManager();
$form = new Form\EmailCampaign\Add($serviceLocator, $objectManager);
if($this->request instanceof HttpRequest && $this->request->isPost())
{
$form->setData($this->request->getPost());
if($form->isValid())
{
$entity = $form->getData();
//If you want to modify a property of the entity (but remember that it's not recommended to do it here, do it in the model instead).
//$entity->setJobId(11);
$model = new Model\EmailCampaign($serviceLocator, $objectManager);
$model->save($entity);
if($entity->getId())
{
$this->flashMessenger()->addSuccessMessage('Email campaign successfully added to the database.');
return $this->redirect()->toRoute('admin/wildcard', ['controller' => 'email-campaign', 'action' => 'edit', 'id' => $entity->getId()]);
}
else
{
$this->flashMessenger()->addErrorMessage('There was an error adding the email campaign to the database. Contact the administrator.');
}
}
}
return new ViewModel
([
'form' => $form,
]);
}
Example of edit action:
public function editAction()
{
$serviceLocator = $this->getServiceLocator();
$objectManager = $this->getObjectManager();
$form = new Form\EmailCampaign\Edit($serviceLocator, $objectManager);
$id = $this->getEvent()->getRouteMatch()->getParam('id');
$entity = $objectManager
->getRepository('Application\Entity\EmailCampaign')
->findOneBy(['id' => $id]);
if($entity)
{
$form->bind($entity);
if($this->request instanceof HttpRequest && $this->request->isPost())
{
$form->setData($this->request->getPost());
if($form->isValid())
{
//If you want to modify a property of the entity (but remember that it's not recommended to do it here, do it in the model instead).
//$entity->setJobId(11);
$model = new Model\EmailCampaign($serviceLocator, $objectManager);
$model->save($entity);
$this->flashMessenger()->addSuccessMessage('Email campaign successfully saved to the database.');
}
}
}
else
{
$this->flashMessenger()->addErrorMessage('A email campaign with this ID was not found in the database.');
return $this->redirect()->toRoute('admin', ['controller' => 'email-campaign']);
}
return new ViewModel
([
'form' => $form,
'entity' => $entity,
]);
}
Hope this helps.
EDIT:
What I provided was an example of how to handle the form and the entities with Doctrine 2 + ZF2.
What you have to keep in mind is that Doctrine doesn't work with the concept of models, it just understands entities. The model I'm using in my application is a concept of the MVC (Model-View-Controller) design pattern (that ZF2 uses) and I have decided to wrap the entity manager calls (persist and flush) inside my model's method, that I named save() (in the case the entity needs some special treatment before being save to the database and also because it is not a good practice to use the entity manager directly in the controller - see this slide of Marcos Pivetta presentation http://ocramius.github.io/presentations/doctrine2-zf2-introduction/#/66).
Another thing that you may be misunderstanding is that when you do $form->getData() to a form that has the DoctrineObject hydrator, it will return you the entity object, and not an array with the data (this last happens if it has no hydrator). So you don't need to create the entity after doing $form->getData(), and if you do so, this created entity won't have any information provided by the form.
Your code should work now:
public function jobSortAction()
{
$serviceLocator = $this->getServiceLocator();
$entityManager = $this->getEntityManager();
$form = new CreateJobSortForm($entityManager);
if ($this->request->isPost())
{
$form->setData($this->request->getPost());
if ($form->isValid()) {
//I'm considering you are setting the DoctrineObject hydrator to your form,
//so here we will get the entity object already filled with the form data that came through POST.
$entity = $form->getData();
//Again, if you need special treatment to any data of your entity,
//you should do it here (well, I do it inside my model's save() method).
//$entity->setJobId(11);
$entityManager->persist($entity);
$entityManager->flush();
}
}
return array('form' => $form);
}
I have database views that have a column that uniquely identifies each row in the view. This column could be used as the primary key even though the view doesn't have a primary key in its definition (DDL) because it's a view.
OpenJPA is refusing to map the views to Java POJOs because there is no primary key.
I have a list of views and primary keys and I have a ReverseCustomizer. Is it possible I can give OpenJPA the column/field to be used as the primary key / id for each view / class?
Currently, the reverse mapping tool calls unmappedTable for each view and I'd like to tell the reverse mapper to do the mapping with the primary key I provide.
Here's something to help you get started.
public class MyDBReverseCustomizer implements ReverseCustomizer {
private ReverseMappingTool rmt;
#Override
public void setTool(ReverseMappingTool rmt) {
this.rmt = rmt;
}
#Override
public boolean unmappedTable(Table table) {
// this method is called to give this class an opportunity to map the
// table which would not be mapped otherwise. Returning false says
// the table wasn't mapped here.
//Class klass = rmt.generateClass(table.getIdentifier().getName(), null);
String packageName = rmt.getPackageName();
String tableName = table.getIdentifier().getName();
String className = NameConverters.convertTableName(tableName);
Class klass = rmt.generateClass(packageName+"."+className, null);
ClassMapping cls = rmt.newClassMapping(klass, table);
Column pk = null;
for ( Column column : table.getColumns() ) {
String columnName = column.getIdentifier().getName();
String fieldName = rmt.getFieldName(columnName, cls);
Class type = rmt.getFieldType(column, false);
FieldMapping field = cls.addDeclaredFieldMapping(fieldName, type);
field.setExplicit(true);
field.setColumns(new Column[]{column});
// TODO: set the appropriate strategy for non-primitive types.
field.setStrategy(new PrimitiveFieldStrategy(), null);
if ("MODEL_VIEW".equals(tableName) && "MODEL_ID".equals(columnName)) {
pk = column;
field.setPrimaryKey(true);
}
customize(field);
}
//cls.setPrimaryKeyColumns(new Column[]{pk});
cls.setObjectIdType(null, false);
cls.setIdentityType(ClassMapping.ID_DATASTORE);
cls.setStrategy(new FullClassStrategy(), null);
return true;
}
...
}
I have two three tables named
Language,Language Type,Employee
Fields of Language Type are LId,LanguageType
Fields Of Language are LId,EmpId,Language fluency
Fields of Employee are EmpId ,EmpName
Relationship between Language Type and language is one –to-one, and language and Employee table is many -to –one. Problem is when I enter data in language it displays the error
A dependent property in a Referential Constraint is mapped
to a store-generated column. Column: 'LId'."}.
Even though data is being inserted in language Type but not in language table .
lINQ query is for language table is
public void AddEmpLanguage(Language language, long id)
{
using (var context = new HRMSEntities())
{
Language emp = new Language
{
EmpId = id,
LanguageFluency = language.LanguageFluency,
};
context.Language.AddObject(emp);
}
}
You can try to add the language directly to a employee object
public void AddEmpLanguage(Language language, long empId)
{
using (var context = new HRMSEntities())
{
Language lang = new Language
{
LanguageFluency = language.LanguageFluency,
};
var employee = context.Employees.Find(empId);
/* or context.Employees.FirstOrDefault(x=>x.ID == empId); */
if(employee != null){
employee.Languages.Add(lang);
}
context.SaveChanges();
}
}