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;
}
}
Related
how can I use a constructor on an Entity that has properties with #ManyToOne decorator and their types are from another Entity but only with the primary key.
For example:
#Entity()
class User {
constructor(
idUser: number,
idExtraData: number
){
this.id = idUser;
// this is going to give an error because it requires an instance of ExtraData.
// but I would like to pass only the id
// and have an instance similar when I use findOne without populating anything
this.extraData = idExtraData;
}
#PrimaryKey()
id: number;
#ManyToOne()
extraData: ExtraData;
}
The instance of the entity I want should be similar to the instance returned by findOne without populating anything.
You can use Reference.createNakedFromPK()
#Entity()
export class Book {
#ManyToOne(() => Author)
author: Author;
constructor(authorId: number) {
this.author = Reference.createNakedFromPK(Author, authorId);
}
}
This instance will be either replaced with the managed entity during flush if there is one, or it will be merged to the EM otherwise.
(the docs mention how to do this with reference wrapper only, but its the same approach)
https://mikro-orm.io/docs/entity-references#assigning-to-reference-properties
I have following table definition
import com.outworkers.phantom.builder.primitives.Primitive
import com.outworkers.phantom.dsl._
abstract class DST[V, P <: TSP[V], T <: DST[V, P, T]] extends Table[T, P] {
object entityKey extends StringColumn with PartitionKey {
override lazy val name = "entity_key"
}
abstract class entityValue(implicit ev: Primitive[V]) extends PrimitiveColumn[V] {
override lazy val name = "entity_value"
}
In concrete table sub class
abstract class SDST[P <: TSP[String]] extends DST[String, P, SDST[P]] {
override def tableName: String = "\"SDS\""
object entityValue extends entityValue
}
Database class
class TestDatabase(override val connector: CassandraConnection) extends Database[TestDatabase](connector) {
object SDST extends SDST[SDSR] with connector.Connector {
override def fromRow(r: Row): SDSR=
SDSR(entityKey(r), entityValue(r))
}
}
The create table query generated by phantom-dsl looks like below
database.create()
c.o.phantom Executing query: CREATE TABLE IF NOT EXISTS test."SDS" (entity_key text,PRIMARY KEY (entity_key))
As you can see derived column is missing from the create table DDL.
Please let me know if I am missing something in the implementation.
Omitted class definitions like SDSR and TSP are simple case classes.
Thanks
Phantom doesn't currently support table to table inheritance. The reasons behind that decision are complexities inherent in the Macro API that we rely on to power the DSL.
This feature is planned for a future release, but until that stage we do not expect this to work, as the table helper macro does not read columns that are inherited basically.
I have a BQL resultset consisting of several left joins, and one of the tables (ARCstRptControl) is joined multiple times under an 'alias' by way of inheriting the original DAC The code is shown below:
public class ARInv : PXGraph<ARInv, ARInvoice>
{
[Serializable]
public class ARCstRptControl1 : ARCstRptControl { }
[Serializable]
public class ARCstRptControl2 : ARCstRptControl { }
foreach (PXResult<ARTran
,PMTran
,ARCstRptControl
,ARCstRptControl1
,PMTask
,Account
,ARCstRptControl2> thistran in PXSelectJoin<ARTran,
LeftJoin<PMTran,
On<ARTran.pMTranID, Equal<PMTran.tranID>>,
LeftJoin<ARCstRptControl,
On<PMTran.origAccountGroupID, Equal<ARCstRptControl.accountID>>,
LeftJoin<ARCstRptControl1,
On<PMTran.accountGroupID, Equal<ARCstRptControl1.accountID>>,
LeftJoin<PMTask,
On<PMTask.projectID, Equal<PMTran.projectID>,
And<PMTask.taskID,Equal<PMTran.taskID>>>,
LeftJoin<Account,
On<ARTran.accountID, Equal<Account.accountID>>,
LeftJoin<ARCstRptControl2,
On<Account.accountGroupID, Equal<ARCstRptControl2.accountID>>>>>>>>,
Where<ARTran.tranType, Equal<Current<ARInvoice.docType>>,
And<ARTran.refNbr, Equal<Current<ARInvoice.refNbr>>>>>.Select(this))
{
ARTran artran = (ARTran)thistran;
PMTran pmtran = (PMTran)thistran;
ARCstRptControl srcgrp = (ARCstRptControl)thistran;
ARCstRptControl1 destgrp = (ARCstRptControl1)thistran;
PMTask pmtask = (PMTask)thistran;
Account account = (Account)thistran;
ARCstRptControl2 destgrp2 = (ARCstRptControl2)thistran;
All the table / DAC variables have the expected results, except ARCstRptControl2. I've moved this entire query into SQL Server and it gives me the results I want, including ARCstRptControl2 - but I must be doing something wrong with the join here in the BQL. Any ideas?
Since I'm using aliased DACs (ARCstRptControl1 and 2), and since I'm using two fields from those aliased DACs, I needed to have those fields (and any others that would be used) from that alias declared in the DAC declaration, as follows (i.e., I'm using accountID and columnID in the join):
[Serializable]
public class ARCstRptControl1 : ARCstRptControl
{
public abstract new class accountID : IBqlField { }
public abstract new class columnID : IBqlField { }
}
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?
POST EDITED - see edit below
I have a query about the FLuent Automapping which is used as part of the SHarp Architecture. Running one of the tests cases will generate a schema which I can use to create tables in my DB.
I'm developing a site with Posts, and Tags associated with these posts. I want a tag to be able to be associated with more than one post, and for each post to have 0 or more tags.
I wanting to achieve a DB schema of:
Post {Id, Title, SubmitTime, Content}
Tag {Id, Name}
PostTag {PostId, TagId}
Instead, I'm getting:
Post {Id, Title, SubmitTime, Content}
Tag {Id, Name, PostID (FK)}
I'm using sharp architecture, and may classes look as follows (more or less):
public class Post : Entity
{
[DomainSignature]
private DateTime _submittime;
[DomainSignature]
private String _posttitle;
private IList<Tag> _taglist;
private String _content;
public Post() { }
public Post(String postTitle)
{
_submittime = DateTime.Now;
_posttitle = postTitle;
this._taglist = new List<Tag>();
}
public virtual DateTime SubmitTime { get { return _submittime; } private set { _submittime = value; } }
public virtual string PostTitle { get { return _posttitle; } private set { _posttitle = value; } }
public virtual string Content { get { return _content; } set { _content = value; } }
public virtual IList<Tag> TagList { get { return _taglist; } set { _taglist = value; } }
public class Tag : Entity
{
[DomainSignature]
private String _name;
public Tag() { }
public Tag(String name)
{
this._name = name;
}
public virtual String Name
{
get { return _name; }
private set { _name = value; }
}
public virtual void EditTagName(String name)
{
this.Name = name;
}
}
I can see why it's gone for the DB schema set up that it has, as there will be times when an object can only exist as part of another. But a Tag can exist separately.
How would I go about achieving this? I'm quite new to MVC, Nhibernate, and SHarp architecture, etc, so any help would be much appreciated!
EDIT*
OK, I have now adjusted my classes slightly. My issue was that I was expecting the intermediate table to be inferred. Instead, I realise that I have to create it.
So I now have (I've simplified the classes a bit for readability's sake.:
class Post : Entity
{
[DomainSignature]
String Title
[DomainSignature]
DateTime SubmitTime
IList<PostTag> tagList
}
class Tag : Entity
{
[DomainSignature]
string name
}
class PostTag : Entity
{
[DomainSignature]
Post post
[DomainSignature]
Tag tag
}
This gives me the schema for the intermediate entity along with the usual Post and Tag tables:
PostTag{id, name, PostId(FK)}
The problem with the above is that it still does not include The foreign key for Tag. Also, should it really have an ID column, as it is a relational table? I would think that it should really be a composite key consisting of the PK from both Post and Tag tables.
I'm sure that by adding to the Tag class
IList<PostTag> postList
I will get another FK added to the PostTag schema, but I don't want to add the above, as the postList could be huge. I don't need it every time I bring a post into the system. I would have a separate query to calculate that sort of info.
Can anyone help me solve this last part? Thanks for your time.
Ok, I'd been led to believe that modelling the composite class in the domain was the way forward, but I finally come across a bit of automapper override code which creates the composite table without me needing to create the class for it, which was what I was expecting in the first place:
public class PostMappingOverride
: IAutoMappingOverride
{
public void Override(AutoMapping map)
{
map.HasManyToMany(e => e.TagList)
.Inverse()
.Cascade.SaveUpdate();
}
}
This will now give me my schema (following schema non simplified):
create table Posts (
Id INT not null,
PublishTime DATETIME null,
SubmitTime DATETIME null,
PostTitle NVARCHAR(255) null,
Content NVARCHAR(255) null,
primary key (Id)
)
create table Posts_Tags (
PostFk INT not null,
TagFk INT not null
)
create table Tags (
Id INT not null,
Name NVARCHAR(255) null,
primary key (Id)
)
alter table Posts_Tags
add constraint FK864F92C27E2C4FCD
foreign key (TagFk)
references Tags
alter table Posts_Tags
add constraint FK864F92C2EC575AE6
foreign key (PostFk)
references Posts
I think the thrower is that I've been looking for a one-to-many relationship, which it is, but it is called HasManytoMAny here...