I've been having trouble with saving embedded collections. I've come up with this crazy solution because I wanted my lazily loaded models to sort based on fields in resources.
I have three tables:
assets (id)
assets_resources (asset_id,resource_id,primary_image)
resources (id,logical_name)
I have defined the tables with hibernate annotations like so:
Assets.java:
#Entity
#Table(name = "assets")
public class Asset implements java.io.Serializable {
#OneToMany
#Cascade(CascadeType.ALL)
#JoinTable(
name="assets_resources"
, joinColumns=#JoinColumn(name="asset_id")
, inverseJoinColumns=#JoinColumn(name="resource_id")
)
#MapKeyColumn(name="asset_id")
#OrderBy("logical_name")
private Map<AssetResource, Resource> resources;
public Map<AssetResource, Resource > getResources() {return resources;}
public void setResources(Map<AssetResource, Resource> resources) {this.resources = resources;}
}
assetResource.java
#Embeddable
public class AssetResource {
private Boolean primary_image;
public Boolean getPrimaryImage() {return primary_image;}
public void setPrimaryImage(Boolean primary_image) {this.primary_image = primary_image;}
private Long id;
public Long getId() {return id;}
}
resource.java
#Entity
#Table(name="resources")
public class Resource implements java.io.Serializable {
...
public String logical_name;
public String getLogicalName() {return logical_name;}
public void setLogicalName(String logical_name) { this.logical_name = logical_name;}
}
I can iterate though the lazily loaded maps in my controllers but I can't seem to save/update embedded fields. specifically, I can't update the primary_image field.
This link demonstrates what I was trying to do.
Are you able to save the attribute value in database. Because i think you won't be able to save the value as u have not mapped it with some column in the database. Try removind Emmbedded and then add the table and column attributes..
Related
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);
}
I'm trying, but have not been successful so far, using the following classes with Hibernate.
#MappedSuperclass
#Embeddable
public abstract class Foo {
// atributes...
}
#Embeddable
public class Poo extends Foo {
// atributes...
}
#Entity
#Table
public class None {
// atributes...
#Embedded
private Foo foo;
// constructor
public None(Foo foo) {
this.foo = foo;
}
}
// example of save
None none = new None(Poo poo);
save(none);
Hibernate returns: Cannot instantiate abstract class or interface
Is it possible to perform this operation with JPA?
I ran into the same problem.
It seems like #embedable does not work with #DiscriminatorColumn. The only way I could get this to work is to use #DiscriminatorColumn, but treat the #embedable like a separate entity on the same table.
What this means is that the query will likely join the table to itself.
#Entity
#Table(name="tbComputers")
public class Computer{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public long id;
public String motherboard;
#OneToOne
#JoinColumn(name="id")
public CPU cpu;
}
#Entity
#DiscriminatorColumn(name="cpu_type")
#Table(name="tbComputers")
public abstract class CPU {
#Id
private Long id;
#Column(name = "cpu")
public String name;
public abstract String someProcessorSpecificMethod();
}
#Entity
#DiscriminatorValue("Intel")
public class Intel extends CPU {
#Override
public String someProcessorSpecificMethod() {
return "Intel";
}
}
#Entity
#DiscriminatorValue("AMD")
public class AMD extends CPU {
#Override
public String someProcessorSpecificMethod() {
return "AMD";
}
}
EDIT: After further testing I found that while this works for reading data, it does not for persisting. It will create a separate INSERT. It seems like it is not supported https://hibernate.atlassian.net/browse/HHH-1910. The alternative is to to split the table.
This is the relevant JPA code:
#MappedSuperClass
public abstract class SuperClass {
#EmbeddedId
private FileId fileId;
protected SuperClass() {
}
public SuperClass(FileId fileId) {
this.fileId = fileId;
}
}
#Embeddable
public class FileId {
protected FileId() {
}
protected File fileName;
public FileId(File fileName) {
this.fileName = fileName;
}
}
#Entity
public MyClass1 extends SuperClass {
#Id
protected String id;
protected MyClass1() {
}
public MyClass1(String id, FileId fileId) {
super(fileId);
this.id = id;
}
}
#Entity
public MyClass2 extends SuperClass {
protected MyClass2() {
}
public MyClass2(FileId fileId) {
super(fileId);
}
}
At runtime I get the following exception:
...
Exception Description: Entity class [class org.abcd.MyClass1] has both an #EmbdeddedId (on attribute [fileId]) and an #Id (on attribute [id]. Both ID types cannot be specified on the same entity.
...
It seems that defining a #EmbeddedId attribute (all attributes of the #Embeddable class) and a #Id attribute together as primary key is not allowed in JPA / Eclipse Link.
Does anybody know a possible solution for this problem?
Any help is appreciated.
Some additional information:
The class MyClassA should contain information (progress, ...; ommited in the code example above) about a certain archive file; the attribute fileId of the super class SuperClass is used to identify this file. Currently fileId (the id-class FileId) only consists of the file name, but later more attributes will be added.
MyClassB contains information about a file IN the archive. This file will be identified with the atttribute id (its the relative path in the archive) and fileId of the super class SuperClass.
I think of the following database structure:
Table "MyClass1":
fileName | id | ...
Table "MyClass2":
fileName | ...
I hope now it's a bit more clear what I exactly want :).
Your mapping doesn't make any sense. You DO have an #EmbeddedId and an #Id in the same class: your MyClassA has na #Id and it extends a SuperClass which already has an #EmbeddedId. I don't know why you would want that? What exactly are you trying to accomplish?
As guys recommended you - maybe changing of the data models will give you more sense here.
And here is my simple proposal :
A Basic class entity containing an ID field and get/set-tters
like :
#MappedSuperclass
public class Entity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// getters/setters
}
This could be used for basic class for all entities in you model.
Then following your functional requirements maybe next could help you:
#Entity
#Inheritance(strategy = InheritanceType.JOIN)
public class File extends Entity {
#Column(unique=true)
private String fileName;
// other future columns you mention above
}
#Entity
public class ArchiveFile extends File {
// other information here - progress and so on
}
#Entity
public class ArchiveFileTracker extends Entity {
#OneToMany
private ArchiteFile architeFile;
// other specific information here
}
I hope this could covers your requirements.
Good luck,
Simeon
I've got two tables A and B with simple PK's.
#Entity
public class A {
#Id
public int idA;
}
#Entity
public class B {
#Id
public int idB;
}
I want to map a new association class AB that simply stores the relations between A and B, with composite PK idA+idB. AB doesn't have any extra columns, just the relation between idA and idB.
Is it possible to map AB using a single class? I want to avoid having to create a new ABId class just to use it as #IdClass or #EmbeddedId in AB, and I don't want to map this with a #ManyToMany association on A or B.
Why do you want to map such a join table? Just use a ManyToMany association between A and B. This join table will then be handled automatically when you'll add/remove a B to/from the list of Bs contained in A.
See http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#d0e11402
If you really want to do that, then just map the two IDs with the #Id notation. The primary class will be the entity class itself (which must be serializable), as explained in the hibernate reference documentation. Note that this is Hibernate-specific.
It would be nice if #JBNizet 's suggestion worked. Unfortunately, there's an old bug which makes it impossible to adopt it in the version I'm using (3.3.1-GA)
I've finally sorted this out by defining an inner static ID class and using it as #IdClass:
#Entity
#Table(name="TABLE_AB")
#IdClass(value=ClassAB.ClassABId.class)
public class ClassAB implements Serializable {
private String idA;
private String idB;
#Id
public String getIdA(){ return idA; }
public void setIdA(String idA){ this.idA = idA; }
#Id
public String getIdB(){ return idB; }
public void setIdB(String idB){ this.idB = idB; }
static class ClassABId implements Serializable {
private String idA;
private String idB;
#Column(name="ID_A")
public String getIdA(){ return idA; }
public void setIdA(String idA){ this.idA = idA; }
#Column(name="ID_B")
public String getIdB(){ return idB; }
public void setIdB(String idB){ this.idB = idB; }
// HashCode(), equals()
}
}
This way I don't have to define a new public class, and I don't have to modify the mappings file to include the ID class.
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