Important: please note that I'm talking about Play! framework version 1 (1.2.6), not 2.x
I have 2 entities in my Play! v1 application, like that:
#Entity
#Table(name = "T_FOO")
public class Foo extends GenericModel {
#Id
#Column(name = "NAME")
public String name;
public static List<Foo> paginate(int start, int count) {
return all().from(start).fetch(count);
}
}
and
#Entity
#Table(name = "T_BAR")
public class Bar extends GenericModel {
#Id
#Column(name = "NAME")
public String name;
public static List<Bar> paginate(int start, int count) {
return all().from(start).fetch(count);
}
}
In my controller, I can do that without problem:
public static void index() {
List<Foo> foo = Foo.paginate(1, 5);
List<Bar> bar = Bar.paginate(2, 5);
render(foo, bar);
}
As you can see, these 2 entities are quite similar, so I created a super class to regroup common methods:
#MappedSuperclass
public class MyGenericModel<T> extends GenericModel {
public static <T> List<T> paginate(int start, int count) {
return all().from(start).fetch(count);
}
}
and make it as the parent of my entities, for example:
#Entity
#Table(name = "T_FOO")
public class Foo extends MyGenericModel<Foo> {
#Id
#Column(name = "NAME")
public String name;
}
However, with this modification, I get the following error when I try to call Foo.paginate() method:
UnsupportedOperationException occured : Please annotate your JPA model with #javax.persistence.Entity annotation.
play.exceptions.JavaExecutionException: Please annotate your JPA model with #javax.persistence.Entity annotation.
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:237)
at Invocation.HTTP Request(Play!)
Caused by: java.lang.UnsupportedOperationException: Please annotate your JPA model with #javax.persistence.Entity annotation.
It seems that Play! wants me to annotate MyGenericModel with #Entity annotation, which I don't want.
I thought that annotating MyGenericModel with #MappedSuperclass would help me to avoid this problem, but it is not the case.
What did I make wrong, and how to make it work correctly?
Thanks.
Unfortunately you can't call the function all() from your class MyGenericModel because that function is enhanced at runtime and only for classes annotated with #Entity.
Possible solution could be that you use your own entity manager for your queries.
Or you could go back to your first option, there was nothing wrong with that :)
Unfortunately (this is also my pain) Generics are not yet supported by Ebean:
https://groups.google.com/forum/#!searchin/ebean/generics/ebean/QWjpI0LRCiA/io-Lm_gfYE4J
Related
I have a Hibernate Entity, BaseEvent, which works fine:
import javax.persistence.*;
#Entity
#Table(name = "base_event")
#SequenceGenerator(name = "seq", allocationSize = 1, sequenceName = "seq")
public class BaseEvent
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
#Column(name = "id")
protected Long id = null;
#Column(name = "my_field", nullable = false)
protected String myField;
public Long getId()
{
return id;
}
public void setId(final Long id)
{
this.id = id;
}
public String getMyField()
{
return myField;
}
public void setMyField(final String myField)
{
this.myField = myField;
}
}
I want to identify when this object is changed and update some Map in my application. The easiest way I could think of doing this was to extend BaseEvent and override the setter:
import java.text.MessageFormat;
public class ExtendedEvent extends BaseEvent
{
#Override
public void setMyField(final String myField)
{
System.out.println(MessageFormat.format("Setting myField to {0}", myField));
super.setMyField(myField);
}
}
This works fine in my application, but then when I come to persist the Entity, Hibernate complains it doesn't know what an ExtendedEvent is.
java.lang.IllegalArgumentException: Unknown entity: my.package.ExtendedEvent
I can see that extending Hibernate Entities is a non-trivial problem, especially when you start adding fields - but all I want is for Hibernate to treat ExtendedEvent as a BaseEvent (because it is). Is there a simple solution for this?
Make base event #MappedSuperclass and extending class #Entity
so
#MappedSuperclass
public class BaseEvent
and
#Entity
#Table(whatever)
public class ExtendedEvent extends BaseEvent
If you want to update your map only when changes are updated in the data store, I would recommend implementing onFlushDirty in a Hibernate Interceptor. This method gets called whenever the Session is flushed to the database for every entity change. You can check the object type in the onFlushDirty method for your entity of interest and property of interest.
Spring Boot seems to be doing something behind the scenes that shouldn't work.
I'm moving a project to Spring Boot and was trying to get spring-boot-starter-data-jpa to work with the fewest changes to the code-base as I could get away with.
Somewhere in the code base, we marshal an xml or json document into an Entity using polymorphic dispatch, and send that entity straight to a database, without knowing exactly what type the entity was.
We have an interface Entity, from which many of our POJOs inherit:
public interface Entity {
Long getId();
void setId(Long id);
}
Example POJO:
#javax.persistence.Entity
#Table
public class Address implements Entity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// fields, getters setters
}
Unfortunately, Spring CrudRepository and JpaRepository don't allow interfaces in the generics signature, so I created an AbstractEntity which inherits from Entity.
#javax.persistence.Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name="descriminatorColumn")
public class AbstractEntity implements Entity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
}
I then created a repository for AbstractEntity:
public interface AbstractEntityRepository<T extends AbstractEntity, ID> extends JpaRepository<T, ID> {
}
I was going to make my Entity POJOs inherit from AbstractEntity, but before I did I noticed that I don't need to.
The following code saves an address to the database:
#SpringBootApplication
#EnableJpaRepositories("com.remsdaq.resque.officersubscription.*")
#EntityScan("com.remsdaq.resque.officersubscription.*")
public class OfficerSubscription implements CommandLineRunner {
private static final Logger LOG = getLogger(OfficerSubscription.class);
#Autowired
private AbstractEntityRepository repository;
public static void main(String[] args) throws Exception {
SpringApplication.run(OfficerSubscription.class, args);
}
#Override
public void run(String... args) throws Exception {
Address address = new Address();
repository.save(address);
}
}
So my question is, how can my AbstractEntityRepository work, when AbstractEntity is not a superclass of Address?
If there's some reasonable reason why this works, and an expectation for this behavior not to change, it might not be too bad to leave it as it is, but without knowing how this is working, it's smelling too much for me to trust it.
Many thanks.
I am creating entities that are the same for two different tables. In order do table mappings etc. different for the two entities but only have the rest of the code in one place - an abstract superclass. The best thing would be to be able to annotate generic stuff such as column names (since the will be identical) in the super class but that does not work because JPA annotations are not inherited by child classes. Here is an example:
public abstract class MyAbstractEntity {
#Column(name="PROPERTY") //This will not be inherited and is therefore useless here
protected String property;
public String getProperty() {
return this.property;
}
//setters, hashCode, equals etc. methods
}
Which I would like to inherit and only specify the child-specific stuff, like annotations:
#Entity
#Table(name="MY_ENTITY_TABLE")
public class MyEntity extends MyAbstractEntity {
//This will not work since this field does not override the super class field, thus the setters and getters break.
#Column(name="PROPERTY")
protected String property;
}
Any ideas or will I have to create fields, getters and setters in the child classes?
Thanks,
Kris
You might want to annotate MyAbstractEntity with #MappedSuperclass class so that hibernate will import the configuration of MyAbstractEntity in the child and you won't have to override the field, just use the parent's. That annotation is the signal to hibernate that it has to examine the parent class too. Otherwise it assumes it can ignore it.
Here is an example with some explanations that may help.
#MappedSuperclass:
Is a convenience class
Is used to store shared state & behavior available to child classes
Is not persistable
Only child classes are persistable
#Inheritance specifies one of three mapping strategies:
Single-Table
Joined
Table per Class
#DiscriminatorColumn is used to define which column will be used to distinguish between child objects.
#DiscriminatorValue is used to specify a value that is used to distinguish a child object.
The following code results in the following:
You can see that the id field is in both tables, but is only specified in the AbstractEntityId #MappedSuperclass.
Also, the #DisciminatorColumn is shown as PARTY_TYPE in the Party table.
The #DiscriminatorValue is shown as Person as a record in the PARTY_TYPE column of the Party table.
Very importantly, the AbstractEntityId class does not get persisted at all.
I have not specified #Column annotations and instead are just relying on the default values.
If you added an Organisation entity that extended Party and if that was persisted next, then the Party table would have:
id = 2
PARTY_TYPE = "Organisation"
The Organisation table first entry would have:
id = 2
other attribute value associated specifically with organisations
#MappedSuperclass
#SequenceGenerator(name = "sequenceGenerator",
initialValue = 1, allocationSize = 1)
public class AbstractEntityId implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "sequenceGenerator")
protected Long id;
public AbstractEntityId() {}
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PARTY_TYPE",
discriminatorType = DiscriminatorType.STRING)
public class Party extends AbstractEntityId {
public Party() {}
}
#Entity
#DiscriminatorValue("Person")
public class Person extends Party {
private String givenName;
private String familyName;
private String preferredName;
#Temporal(TemporalType.DATE)
private Date dateOfBirth;
private String gender;
public Person() {}
// getter & setters etc.
}
Hope this helps :)
Mark the superclass as
#MappedSuperclass
and remove the property from the child class.
Annotating your base class with #MappedSuperclass should do exactly what you want.
This is old, but I recently dealt with this and would like to share my solution. You can add annotations to an overridden getter.
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Column(name = "id", nullable = false, updatable = false)
#Id
private ID id;
public ID getId() {
return id;
}
...
}
#Entity
#Table(name = "address")
public final class Address extends AbstractEntity<UUID> implements Serializable {
...
#Override
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
public final UUID getId() {
return super.getId();
}
...
}
My simplified model looks like this:
#Entity public class Aspect extends Model {
#Id public Long id;
#OneToMany(cascade = CascadeType.ALL) public List<Restriction> restrictions;
}
#Entity public class Restriction extends Model {
#Id public Integer id;
#ManyToOne public RestrictionTemplate restrictionTemplate;
}
#Entity public class RestrictionTemplate extends Model {
#Id private Integer id;
}
Basically the idea is this: each Aspect has a set of Restrictions. And each Restriction itself relies on a RestrictionTemplate.
I want Aspect creation form to like this: user can select some RestrictionTemplates and on form submit new Restrictions should be created and associated with new Aspect.
Let me explain once again: On form submission I want to create Aspect and relating Restrictions based on RestrictionTemplate's ids provided.
Whicn names should the fields in the form have in order to make such binding possible?
The naming which works for direct relantionships:
restrictions[0].restrictionTemplate.id
restrictions[1].restrictionTemplate.id
doesn't work here (creates Aspect entry in DB, but no Restriction entries).
I think you simply must write a bit of code for that, in which you search for the RestrictionTemplates corresponding to the passed IDs and then assigning them to the new instance of Aspect:
List<RestrictionTemplates> templates = new ArrayList<RestrictionTemplates>();
for (int crtTplId : passedIds) {
templates.add(entityManager.find(RestrictionTemplates.class, crtTplId));
}
List<Restriction> restrictions = new ArrayList<Restriction>();
for (RestrictionTemplates crtTpl : templates) {
restrictions.add(new Restriction(crtTpl));
}
Aspect aspect = new Aspect();
aspect.restrictions = restrictions;
entityManager.persist(aspect);
PS: as I understand, there can be Restrictions that do not belong to an Aspect. If that is not true, than you should make your Aspect-Restriction relationship bilateral, Restriction being the owning side:
#Entity public class Aspect extends Model {
#Id public Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy="aspect") public List<Restriction> restrictions;
}
#Entity public class Restriction extends Model {
#Id public Integer id;
#OneToMany(/*+ add the #JoinColumn for marking the column not nullable*/) public Aspect aspect;
#ManyToOne public RestrictionTemplate restrictionTemplate;
}
I'm getting this Hibernate error:
org.hibernate.MappingException: Could not determine type for:
a.b.c.Results$BusinessDate, for columns: [org.hibernate.mapping.Column(businessDate)]
The class is below. Does anyone know why I'm getting this error??
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"businessDate"
})
#XmlRootElement(name = "Results")
#Entity(name = "Results")
#Table(name = "RESULT")
#Inheritance(strategy = InheritanceType.JOINED)
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Results implements Equals, HashCode
{
#XmlElement(name = "BusinessDate", required = true)
protected Results.BusinessDate businessDate;
public Results.BusinessDate getBusinessDate() {
return businessDate;
}
public void setBusinessDate(Results.BusinessDate value) {
this.businessDate = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"raw",
"display"
})
#Entity(name = "Results$BusinessDate")
#Table(name = "BUSINESSDATE")
#Inheritance(strategy = InheritanceType.JOINED)
public static class BusinessDate implements Equals, HashCode
{
....
Update: This code was generated by HyperJaxB. So I don't claim to understand it all, just trying to make some changes to it!
Update2: Here's the full (yah it's big) src file
Using a static nested class as a field type is fine and supported. But Hibernate won't know how to map such a complex type to a column type (which is what the error message says).
So you'll need either to create a user type to handle this or to annotate the Results.BusinessDate field with a #OneToOne annotation to persist it in another table (I would also remove the #Inheritance which is useless but this is not the problem here).
Update: Just to clarify, using a user type or mapping the complex type with #OneToOne does work. The following code works perfectly (tested):
#Entity
public class EntityWithStaticNestedClass implements Serializable {
#Id
#GeneratedValue
private Long id;
#OneToOne
private EntityWithStaticNestedClass.StaticNestedClass nested;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public EntityWithStaticNestedClass.StaticNestedClass getNested() {
return nested;
}
public void setNested(EntityWithStaticNestedClass.StaticNestedClass nested) {
this.nested = nested;
}
#Entity
public static class StaticNestedClass implements Serializable {
#Id
#GeneratedValue
private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
}
And both entities get well persisted in their respective tables. But you're not showing the entire code nor the exact error so I can't say why it didn't for you (maybe you're missing #Id etc).
That being said, if you don't want businessDate to be persisted at all, annotate it with #Transient (with JPA, fields are persistent by default):
Update: You can't mix field and property access. So you need to annotate getBusinessDate() with #Transienthere. Sorry, I couldn't guess that from the shown code and I thought it would be obvious.
Same comment as Kevin Crowell. You might also look at not using inner classes for entity types. I've actually never seen someone do that with Hibernate, so I'm not sure if it's even possible, or how you would map it.
The #Inheritance annotation on the BusinessDate inner class seems a little fishy too - the inner class is static, and does not inherit from another entity, unless Hibernate treats inner classes as "inherited."
Overall, not really sure what you're trying to accomplish, but you might be making your life harder than it should be. I would recommend not using inner classes, and just mapping all the entities in a more simple/straightforward fashion.