OpenXava populating Collection from database - openxava

I have a problem populating a detail view from the database.
application.xml snippet:
<module name="ModuleB">
<model name="B"/>
<view name="ViewB"/>
...
<mode-controller name="DetailOnly"/>
</module>
I have three entity classes:
#Entity
#Table(name="A")
class A {
#OneToMany(mappedBy = "a")
#ListProperties("col1")
#CollectionView("cs")
private Collection<C> cs;//+getter/setters
}
#Entity
#Table(name="A")
#View(name = "ViewB", ...)
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<C> cs;//+getter/setters
}
#Entity
#Table(name="C")
#View(name = "ViewC", ...)
class C {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
}
I want to read an existing B instance from the database by clicking on a link, then editing/modify it.
When I set the model object of the view with getView().setModel() or even using getView().findObject(), on the screen everything looks good, the collection shows its proper content.
On the other hand when i try to save it back, in the save action the getView().getEntity().getCs() collection is null.
What do I have to do to make the view being correspond to the entity behind?
I am using OpenXava 5.0.1, java 1.7
Important note: I am not allowed to change OpenXava version, since it is a legacy project.
My editing (20170126)
I've made a new class to avoid a reference problem:
#Entity
#Table(name="C")
#View(name = "ViewC", ...)
class D {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private B a;
}
and modified the class B accordingly:
#Entity
#Table(name="A")
#View(name = "ViewB", ...)
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<D> cs;//+getter/setters
}
But the result is the same: The records of the subview (Collection ViewC) are loaded from the DB,
and shown on the screen correctly, but I get an error message if I want to edit the Collection ViewC
( eg: add a new entry ):
"Impossible to execute Save action: Impossible to create: an object with that key already exists"
+ As before: in the save action the getView().getEntity().getCs() collection is null

OpenXava uses JPQL to obtain collection data instead of just calling the collection getter, this is for allowing filtering and ordering. The problem here is that the query sentence generated by OpenXava gets the data but the getter of the JPA entity not. Surely JPA/Hibernate is not very happy with a mappedyBy to a reference to another type. That is you write:
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<C> cs;//+getter/setters
}
Where a is a reference to A, not to B.
Given that A and B map the same table, why not to use just one entity?

Related

How to get prices with ProductEntity?

I created my own Entity called "MyProductEntity" (and also Definition/Collection) which has a relation to some Product (eg. a product is linked with another product)
The problem is that MyProductEntity->connectedProduct doesn't contain prices (eg. calculatedPrices). Does anyone know how to load the prices? I read somewhere that I should use SalesChannelProductEntity instead of ProductEntity? But I am not sure how? Any hints?
Here are (I hope) the relevant parts from my code:
class MyProductEntity extends Entity
{
protected string $productId;
protected string $productVersionId;
protected ProductEntity $product;
protected string $connectedProductId;
protected string $connectedProductVersionId;
protected ProductEntity $connectedProduct;
//I tried to use here "SalesChannelProductEntity" instead of "ProductEntity"
//...
//other properties
}
in some subscriber: (eg.: for ProductPageLoadedEvent::class) I load MyProductEntity like this:
public function getMyProducts(ProductPageLoadedEvent $event): array
{
$page = $event->getPage();
$salesChannelContext = $event->getSalesChannelContext();
$product = $page->getProduct();
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('productId', $product->getId()));
$criteria->addAssociation('connectedProduct');
$myProducts = $this->myProductRepository->search($criteria, $salesChannelContext);
//$this->myProductRepository = defined via services.xml / in constructor:
// EntityRepository $myProductRepository
}
As you already found out the field calculatedPrices and similar are exclusive to the SalesChannelProductDefinition and SalesChannelProductEntity. You could simply create your own sales channel definition in addition to the regular definition:
class SalesChannelMyProductDefinition extends MyProductDefinition implements SalesChannelDefinitionInterface
<service id="Foo\MyPlugin\SalesChannel\SalesChannelMyProductDefinition">
<tag name="shopware.sales_channel.entity.definition"/>
</service>
Then use the repository of your new sales channel definition:
$myProduct = $this->getContainer()->get('sales_channel.my_product.repository')
->search($criteria, $salesChannelContext)->first();
// should then be an instance of SalesChannelProductEntity
$salesChannelProduct = $myproduct->getConnectedProduct();

Passing Interfaces into Dictionary key's

I have been following following coreclr for a little while and I am new to programming. My question is why do they pass interfaces into Dictionary's especially the key value?
//
// Allocate a new Dictionary containing a copy of the old values, plus the new value. We have to do this manually to
// minimize allocations of IEnumerators, etc.
//
Dictionary newValues = new Dictionary(current.m_localValues.Count + (hadPreviousValue ? 0 : 1));
My understanding is that interface is to implemented by a class. Once implemented it can call/use functions or store data in the classes properties/ variables. I am missing some understanding of interfaces and their use cases but I do not know what that it.
Why do you instantiate a variable to an interface or pass an interface into a parameter? My understanding is you will then have an instance of that variable which still can't hold values nor change state through methods.
Let me explain.
Interface is contract. It just contains method without implementation. Now it may possible that that interface is being implemented by any number of class.
public interface IEntity { int Id {get;set;} }
public class Student : IEntity { public int Id {get;set;} // Interface Property }
public class Teacher : IEntity { public int Id {get;set;} // Interface Property }
Dictionary<IEntity,object> obj = new Dictionary<IEntity,object>(); Student s = new Student(); Teacher t = new Teacher(); obj.Add(s,any object); obj.Add(t,any object);
This is because of interface that your dictionary can hold reference of both type ( Student and Teacher).
In .NET when any object is created it is uniquely identify by GetHashCode() method. // You can find more detail on this on MSDN.
Also Dictionary not means that keys must be only primitive type. This is the reason it is good if you have more than one key ( Like composite key in Database) so it allow you to identify uniquely based on your custom implementation.
Now second Generic.
public class PersonInfo<T> where T : IEntity
{
public string Name {get;set;}
public T Entity {get;set;}
}
PersonInfo<Student> student = new PersonInfo<Student>();
student.T = new Student();
student.Name = "";
PersonInfo<Teacher> Teacher = new PersonInfo<Teacher>();
teacher.T= new Teacher();
teacher.Name = "";
When you have interface. It not actually interface. You always have a reference to object with that interface. And that object is the one responsible for comparison in dictionary
The benefit is not difference from using class as a key. Dictionary can be used as list to iterate KeyValuePair to take key to do some operation. But using interface means you can store various type of class with same interface instead of just one type. Which is decoupled and more flexible

'Unexpected element: XX' during deserialization MongoDB C#

I'm trying to persist an object into a MongoDB, using the following bit of code:
public class myClass
{
public string Heading { get; set; }
public string Body { get; set; }
}
static void Main(string[] args)
{
var mongo = MongoServer.Create();
var db = mongo.GetDatabase("myDb");
var col = db.GetCollection<BsonDocument>("myCollection");
var myinstance = new myClass();
col.Insert(myinstance);
var query = Query.And(Query.EQ("_id", new ObjectId("4df06c23f0e7e51f087611f7)));
var res = col.Find(query);
foreach (var doc in res)
{
var obj = BsonSerializer.Deserialize<myClass>(doc);
}
}
However I get the following exception 'Unexpected element: _id' when trying to Deserialize the document.
So do I need to Deserialize in another way?? What is the preferred way of doing this?
TIA
Søren
You are searching for a given document using an ObjectId but when you save an instance of MyClass you aren't providing an Id property so the driver will create one for you (you can make any property the id by adding the [BsonId] attribute to it), when you retrieve that document you don't have an Id so you get the deserialization error.
You can add the BsonIgnorExtraElements attribute to the class as Chris said, but you should really add an Id property of type ObjectId to your class, you obviously need the Id (as you are using it in your query). As the _id property is reserved for the primary key, you are only ever going to retrieve a single document so you would be better off writing your query like this:
col.FindOneById(new ObjectId("4df06c23f0e7e51f087611f7"));
The fact that you are deserializing to an instance of MyClass once you retrieve the document lends itself to strongly typing the collection, so where you create an instance of the collection you can do this
var col = db.GetCollection<MyClass>("myCollection");
so that when you retrieve the document using the FindOneById method the driver will take care of the deserialization for you putting it all together (provided you add the Id property to the class) you could write
var col = db.GetCollection<MyClass>("myCollection");
MyClass myClass = col.FindOneById(new ObjectId("4df06c23f0e7e51f087611f7"));
One final thing to note, as the _id property is created for you on save by the driver, if you were to leave it off your MyClass instance, every time you saved that document you would get a new Id and hence a new document, so if you saved it n times you would have n documents, which probably isn't what you want.
A slight variation of Projapati's answer. First Mongo will deserialize the id value happily to a property named Id which is more chsarp-ish. But you don't necessarily need to do this if you are just retrieving data.
You can add [BsonIgnoreExtraElements] to your class and it should work. This will allow you to return a subset of the data, great for queries and view-models.
Try adding _id to your class.
This usually happens when your class doesn't have members for all fields in your document.
public class myClass
{
public ObjectId _id { get; set; }
public string Heading { get; set; }
public string Body { get; set; }
}

EclipseLink Descriptor Customizer & History Policy and JSF. How to insert user principals in history?

In my JSF web application, I use EclipseLink
Descriptor Customizer
and
History Policy
to populate a history table in database.
The corresponding JPA entity class is annotated with #Customizer(beans.HistoryLesionhCustomizer.class)
The history table has the same fields as the source table, plus two fields (start_date & end_date) to specify the start and end of operation on a row.
It is fully working. But what I need is to populate another field in the history table.
This field I called user, should be populated with the User Principals, and this will allow me to trace the user who performed the CUD (Create/Update/Delete) operation.
I thought History Policy would allow me to add a field by just indicating its corresponding name in the database and indicate the object value that must be inserted. But that is not the case, or may it be I am not able to figure how this can be done.
In other words, along with start_date and end_date, i want to populate user field with :
FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
package beans;
/**
* Whenever there is a change on a record or an insert, change will be traced.
* #author mediterran
*
*/
import javax.faces.context.FacesContext;
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.history.HistoryPolicy;
public class HistoryLesionhCustomizer implements DescriptorCustomizer {
#Override
/**
* Implementation method to use
*/
public void customize(ClassDescriptor cd) throws Exception {
String user = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
HistoryPolicy policy = new HistoryPolicy(); // Instantiates a new policy
//policy.postUpdate();
policy.useDatabaseTime(); // Use the default database time to avoid date conflict
policy.addHistoryTableName("history_lesionh"); // Indicate the source table name
policy.addStartFieldName("start_date"); // indicate the start date DB column
policy.addEndFieldName("end_date"); // Indicate the end date DB column
cd.setHistoryPolicy(policy); // Use the Policy for the entity class where used #Customizer(HistoryLesionhCustomizer.class)
}
}
Any help or workarounds would be appreciated.
Thanks
Unfortunately HistoryPolicy only adds start and end date. But you can add user information to your entity with the help of an EntityListeners . Here is an example. It will add user information to each persist/update of the customer table:
import javax.persistence.EntityListeners;
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "customer")
public class Customer implements Serializable {
#Column(name = "User")
private String user;
// getter and setter
}
and the AuditListener:
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
//...
public class AuditListener {
#PrePersist
#PreUpdate
public void setUserInformation(Object entity) {
if (entity instanceof Customer) {
Customer myEntity = (Customer) entity;
myEntity.setUser(FacesContext.getCurrentInstance().getExternalContext().getRemoteUser());
}
}
}
If you have more than one column that needs user information, you can use a MappedSuperclass entity and put the user column in this class. Then let all your auditable entities extend this MappedSuperclass and check in the AuditListener if the entity is an instance of the superclass.

Populating field from enum table

I have the following tables
Entity
id,name,categoryid
21,"Blah",1
EntityCategory(Enum table)
id, name
1,"New Blahs"
I have a FK relationship between Entities->categoryid and EntityCategories->id
I have generated SubSonic classes for both as well a corresponding Model object for Entity
class Entity{ID,Name,CategoryName}
I am trying to return the Model.Entity type with category name filled in i.e.
public Entity GetEntityByName(string name){
return new
Select(
Entity.IdColumn,
Entity.NameColumn,
EntityCategory.NameColumn)
.From(Entity.Schema)
.InnerJoin(Tables.EntityCategory)
.Where(Entity.NameColumn).IsEqualTo(name)
.ExecuteSingle<Model.Entity>();
Needless to say this is not working. I actually get a Model.Entity with the Entity.Name set to the EntityCategoryName.
If you use SubSonic 3.0 you can do this with projection:
var result = from e in db.Entities
where e.ID=1
select new Entity{
ID=e.ID,
name=e.Name,
CategoryName=(CategoryName)e.CategoryID
}
With SubSonic 2.x, I'd say to make it easy on yourself and extend the partial class with a readonly enum:
public partial class Entity{
public CategoryName{
return (CategoryName)this.CategoryID;
}
}

Resources