I am trying to create Ebean views on tables based on a simple join and I am running into issues when I try to extend the Model for the base table.
The Views fields and the Models fields are the exact same.
My table Model looks like this:
#Entity
#Table(name = "assets")
public class Asset extends EnvironmentModel<Integer> {
#Id
#Column
#PrimaryKey
#Attribute(index = 0)
private int assetId;
#Column
#Attribute(index = 1)
private String make;
etc...
}
That works just fine.
Now what I am trying to to do with the View is:
#View(name = "assets_view")
public class AssetView extends Asset {
}
I thought I might be able to do this because the AssetView and the Asset having the same exact fields.
When I do it this way I get the exception:
Caused by: javax.persistence.PersistenceException: models.asset.AssetView is NOT an Entity Bean registered with this server?
So my next attempt was to add the #Entity annotation to the View class. e.g.
#Entity
#View(name = "assets_view")
public class AssetView extends Asset {
}
I get the following exception when compiling:
Error injecting constructor, java.lang.IllegalStateException: Checking class models.asset.AssetView and found class models.asset.Asset that has #Entity annotation rather than MappedSuperclass?
But I can't remove the #Entity annotation from my Asset class because I need that to do inserts.
My questions is:
Is there any way to a have a view and a table share the same model, so I can query from the view and insert/update into the table?
Ok, I found an answer and I don't know if this is obvious.
Basically, I just made my base class a #MappedSuperClass e.g.
#MappedSuperclass
public class _Asset extends EnvironmentModel<Integer> {
#Id
#Column
#PrimaryKey
#Attribute(index = 0)
private int assetId;
#Column
#Attribute(index = 1)
private String make;
etc..
}
Then I extended my Asset table and AssetView from that Mapped super class e.g.
#Entity
#Table(name = "assets")
public class Asset extends _Asset {
}
--
#Entity
#View(name = "assets_view")
public class AssetView extends _Asset {
public static final Model.Find<Integer, AssetView> finder = new Model.Finder<>(AssetView.class);
}
Related
Im my SQL Server Database I have 8 tables with the same structure.
Now I want to insert in selected tables with one Java class.
#Entity
#Table(name = "tbl_Prognosen") //here I want to put all table-Names
public class AZBNachricht { ...
is this possible?
It isn't possible to accomplish what you described.
The closest to code reuse at the entity class level would be to use a #MappedSuperclass class where you place all the shared column names, etc and then extend that for each table implementation with differing table names.
#MappedSuperclass
public abstract class AbstractStructure {
#Id
#GeneratedValue;
private Integer id;
private String column1;
private String column2;
}
#Entity
#Table(name = "table1")
public class Entity1 extends AbstractStructure {
}
// ... so on
I have an abstract class to represent a type of settings. The inheritance type is in a single table as I wish to be able to access all types of settings irrespective of concrete type. Here is my parent abstract class:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
public abstract class Settings extends Model {
#Id
public Long settingId;
public static Model.Finder<Long, Settings> find = new Model.Finder<>(Long.class, Settings.class);
public abstract void run();
}
This is one of my concrete types:
#Entity
#DiscriminatorValue("text")
public class TextSettings extends Settings {
public boolean type;
#OneToOne(cascade = CascadeType.ALL)
public EmailFields emailFields;
public static Finder<Long, TextSettings> find = new Finder<>(Long.class, TextSettings.class);
public static TextSettings get() {
if (find.all().size() > 0)
return find.all().get(0);
else {
TextSettings settings = new TextSettings();
settings.emailFields = new EmailFields();
settings.emailFields.test = "Test"; \\this field is null if you try to get this field with a get on the TextSettings ebean object
settings.save();
return settings;
}
}
}
This concrete type actually contains another ebean model with the OneToOne relationship. Here is the code for that model:
#Entity
#DiscriminatorValue("email")
public class EmailFields extends Model {
#Id
public Long id;
public String test;
public static Finder<Long, EmailFields> find = new Finder<>(Long.class, EmailFields.class);
}
When I try to get the EmailFields model through the TextSettings model, I get the correct id and the object exists in the database, but the field test is null. Any field I add to it is always null.
This type of set up works for me in a non-inheritance ebean model so I can only think it has something to do with the single table. Does anyone know a solution for this, or will I have to copy the test field into the TextSettings model?
Note: I have simplified the code so logically it might not make sense as to why I have one field in EmailFields but the assumption is that I do need it as a separate model as some settings will have this model and some won't. So I don't want boilerplate code in those settings' classes.
Update
So for now I am using the #Embedded and #Embeddable annotations.
#Embeddable
public class EmailFields extends Model
And in TextSettings
#Embedded
public EmailFields emailFields;
This simply copies EmailFields' fields into the TextSettings object and not as a separate entity. Only drawback with this is that it increases the size of the table.
I have situation where I have some legacy tables.
Assets, with composite PK (assetId, fiscalId, recordType)
AssetSystems, with composite PK (assetId, fiscalId, systemId)
And they have a relationship.
Asset 1 - 1 AssetSystems
where, Systems FK (assetId, fiscalId)
Here's my code.
Asset.java
#Entity
#Table(name="Assets")
public class Asset implements Serializable {
#EmbeddedId
private AssetKey compositeKey;
#OneToOne(fetch=FetchType.LAZY, mappedBy="asset")
private AssetSystem assetSystem;
// other fields and setters - getters
}
AssetKey.java
#Embeddable
public class AssetKey implements java.io.Serializable {
#Column(name="assetID")
private String assetID;
#Column(name="fiscalPeriodID")
private BigInteger fiscalPeriodID;
#Column(name="recordType")
private String recordType;
// setter - getter
}
AssetSystem.java
#Entity
#Table(name="AssetSystems")
public class AssetSystem implements Serializable {
#EmbeddedId
private AssetSystemKey compositeKey;
#OneToOne
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name="assetID", referencedColumnName="assetID"),
#PrimaryKeyJoinColumn(name="fiscalPeriodID", referencedColumnName="fiscalPeriodID")
})
private Asset asset;
// other fields and setter - getter
}
AssetSystemKey.java
#Embeddable
public class AssetSystemKey implements Serializable {
#Column(name="assetID")
private String assetID;
#Column(name="fiscalPeriodID")
private BigInteger fiscalPeriodID;
#Column(name="systemID")
private BigInteger systemID;
// setter - getter
}
When I try to access the DB, I get this exception
java.lang.IllegalArgumentException:
org.hibernate.TypeMismatchException: Provided id of the wrong type for
class com.wb.adapter.model.AssetSystem. Expected: class
com.wb.adapter.model.AssetSystemKey, got class
com.wb.adapter.model.AssetKey at
org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:274) at
com.wb.adapter.main.AssetDbReader.readDB(AssetDbReader.java:47) at
com.wb.adapter.main.AssetDbReader.main(AssetDbReader.java:30) Caused
by: org.hibernate.TypeMismatchException: Provided id of the wrong type
for class com.wb.adapter.model.AssetSystem. Expected: class
com.wb.adapter.model.AssetSystemKey, got class
com.wb.adapter.model.AssetKey
I have also tried replacing the PrimaryKeyJoinColumns with simple JoinColumn which results in this exception.
Caused by: org.hibernate.AnnotationException:
referencedColumnNames(assetID, fiscalPeriodID) of
com.wb.adapter.model.AssetSystem.asset referencing
com.wb.adapter.model.Asset not mapped to a single property
I am new to JPA and not sure what am I doing wrong. Also, is there a better way to model the above mentioned tables in JPA, which will in effect resolve this issue.
Many thanks in advance.
Sahil
Try to replace #PrimaryKeyJoinColumns with #JoinColumns and add #MapsId("compositeKey") for Asset reference.
I have two hibernate classes: a base class, and an extended class that has additional fields. (These fields are mapped by other tables.)
For example, I have:
#Entity
#Table(name="Book")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
public class B extends A {
public String node_ID;
// ...
}
public class Node {
public String ID; // maps to B.node_ID
// ...
}
How do I map this in Hibernate? The hibernate documentation states three types of inheritence configurations: one table per class, one table with a type column, and a join table -- none of which apply here.
The reason I need to do this is because class A is from generic framework that's reused over multiple projects, and class B (and Node) are extensions specific to one project -- they won't be used again. In the future, I may have perhaps a class C with a house_ID or some other field.
Edit: If I try the above pseudo-code configuration (two entities mapped to the same table) I get an error that the DTYPE column doesn't exist. The HQL has a "where DTYPE="A" appended.
This is possible by mapping the #DiscriminatorColumn and #DiscriminatorValue to the same values for both classes; this can be from any column you use that has the same data regardless of which type (not sure if it works with null values).
The classes should look like so:
#Entity
#Table(name="Book")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="published")
#DiscriminatorValue(value="true")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
#DiscriminatorValue(value="true")
public class B extends A {
public String node_ID;
// ...
}
For anyone who got here like me and does not want to have the dtype column but instead want to use the same table for more than one entity as is I would recommend using this
Basically you can create a Base like this
#MappedSuperclass
public abstract class BaseBook<T extends BaseBook> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
... any other variables, getters + setters
}
#Entity
#Table(name= "book")
public class BookA extends BaseBook<BookA>{
//Default class no need to specify any variables or getters/setters
}
#Entity
#Table(name= "book")
public class BookB extends BaseBook<BookB>{
#Column(name = "other_field")
private String otherFieldInTableButNotMapedInBase
... Any other fields, getter/setter
}
From the above we have created base super class which does not have any entity or table mapping. We then create BookA to be default with the Entity + Table mapping. From there we can create other Entities all extending from BaseBook but pointing to one table
I have 4 persistent classes which all have the same fields (exactly) the only 3 difference between them is 1) the class name, 2) the table name and 3) the data. i am aware that this might seem strange to some but trust me there is a good reason which i won't go into here.
now, i'm using hibernate annotations to configure my class which should work like so:
#Entity
#Table(name = "store")
public class Store
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
.. and this does work, for a single stand-alone class, however there are many fields to map and i'd like to do it all in one hit for all four similar classes, ie:
public class StoreBase
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
#Entity
#Table(name = "store1")
public class Store1 extends StoreBase
{}
#Entity
#Table(name = "store2")
public class Store2 extends StoreBase
{}
#Entity
#Table(name = "store3")
public class Store3 extends StoreBase
{}
#Entity
#Table(name = "store4")
public class Store4 extends StoreBase
{}
however when attempting this i get the following exception:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: package.entities.Store1
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:672)
at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:546)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1292)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
i'm guessing this is because the super class is not being searched for the identifier?
is there a way to utilise inheritance in this context?
thanks, paul.
#MappedSuperclass
public class StoreBase
See docs for more info.
Have a look at #MappedSuperclass.