All tables in our shop have the same set of identical audit columns(system, user, terminal, etc)
So instead of copying the same columns in every class I tried to use the embedded annotation
#Entity
#Table(name="PRODUCT")
public class ProdEntity implements Serializable{
private long prod;
//set/get code
private CommonJrnFields jrn;
#Embedded
public CommonJrnFields getJrn() {return this.jrn;}
public void setJrn(CommonJrnFields jrn) {this.jrn = jrn;}
private OrdEntity ord;
#OneToOne(mappedBy="prod")
public OrdEntity getOrd() {return this.ord;}
public void setOrd(OrdEntity ord) {this.ord = ord;}
}
#Entity
#Table(name="ORDERS")
#NamedQuery(name="OrdEntity.findAll", query="SELECT o FROM OrdEntity o")
public class OrdEntity extends ProdEntity implements Serializable{
private long ord;
//set/get code
private CommonJrnFields jrn;
#Embedded
public CommonJrnFields getJrn() {return this.jrn;}
public void setJrn(CommonJrnFields jrn) {this.jrn = jrn;}
private ProdEntity prod;
#OneToOne(mappedBy="ord")
public ProdEntity getProd () {return this.prod;}
public void setProd(ProdEntity prod) {this.prod = prod;}
}
#Embeddable
public class CommonJrnFields {
private String user;
private String system;
//set/get code
}
As a result embedded columns of extended class overrides base class and named query returns
ProdEntity.prod
OrdEntity.ord
OrdEntity.system
OrdEntity.user
instead of
ProdEntity.prod
ProdEntity.system
ProdEntity.user
OrdEntity.ord
OrdEntity.system
OrdEntity.user
How to fix it?
Please do not offer the #AttributeOverrides solution.
I'm working with almost 100 tables and do attribute override in each class for 8 common columns specifying table="" does not make any sense. It would be much faster just to cut/paste the embeddable code into all entities if there is no other solutions.
Related
I'm using Neo4j ogm to map many (over 20) similar classes into neo4j db which are different in just
relationship name, name and direction.
Each class implements the "Classification" interface with just one method which is the same
in every class (consist on adding relation into collection of node)
Example node:
#NodeEntity
public class ExampleClass implements Classification {
#GraphId
private Long id;
private String name;
#Relationship(type = "EXAMPLE_1", direction = "OUTGOING")
private Set<Species> classification = new HashSet<>();
public ExampleClass (){}
public ExampleClass (String name) {
this.name = name;
}
public Set<Species> getClassification(){
return classification;
}
#Override
public void specifiedAs(Species species){
classification.add(species);
}
and analogously:
#NodeEntity
public class ExampleClass2 implements Classification {
#GraphId
private Long id;
private String name;
#Relationship(type = "EXAMPLE_2", direction = "OUTGOING")
private Set<Species> classification = new HashSet<>();
public ExampleClass2 (){}
public ExampleClass2 (String name) {
this.name = name;
}
public Set<Species> getClassification(){
return classification;
}
#Override
public void specifiedAs(Species species){
classification.add(species);
}
}
I'm looking for possibility to reduce count of those similar classes
and create... maybe one generic class in which I can define label,property name and realtionship type also.
I prefer still using spring-data and Neo4j OGM.
You could improve this by introducing an super class containing all the common properties, and just have the specific relationships in your ExampleClassXXX classes.
Note that the relationship types cannot be dynamic, so you cannot have just a generic class by itself.
i am working on a web application using Spring, Hibernate and SpringMVC,
i am facing a problem with retreiving values from a subclass table using SingleTable inheritance strategy, here are my entities
Client.java (Super class)
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "typeClient", discriminatorType = DiscriminatorType.STRING)
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idClient;
private String matricule;
private String statut;
private String secteurDactivite;
private String nomClient;
private String emailClient;
private String numTelephone;
private String adresse;
//constructor
//getter & setters
}
Societe.java (subClass1)
#Entity
#DiscriminatorValue("Societe")
public class Societe extends Client implements Serializable{
private String nomResponsable;
private String emailResponsable;
private String telResponsable;
private String nomSuperieur;
private String emailSuperieur;
private String telSuperieur;
private String commentaire;
//constructeur sans parametre
public Societe() {
}
}
Particulier.java (subclass2)
#Entity
#DiscriminatorValue("Particulier")
public class Particulier extends Client implements Serializable {
private String cin;
//constructeur sans parametres
public Particulier() {
}
}
in my implementation i am using this methode to get a particular client with his ID
ClientDaoImpl.java
public class ClientDaoImpl implements ClientDao {
#PersistenceContext
private EntityManager em;
#Override
public Client getClientByID(int id_client) {
return em.find(Client.class, id_client);
}
When i ran this code i only selected the attributes of the superClass Client.
what i am trying to do is to get a client with its subclass whether it's a Societe or Particulier based on its type or clientID.
Please Help
As you don't know the type of client before querying and only it's ID, you will need to inspect the type and cast after you retrieve the record;
Client client1 = clientDao.getClientById(clientID);
if (client1 instanceof Societe) {
((Societe) client1).getCommentaire();
}
Depending on your use case, it may be useful to map the result of the client query to a ClientDescriptor object which contains all the fields for all client types and returns either nulls or blanks. This means you don't have to keep checking for client type everywhere;
public class ClientDTO {
//client fields
private String nomResponsable = "";
....
//subclass 1 fields.... initialize to empty
//subclass 2 fields .... initialize to empty
public ClientDTO (Client client) {
// set fields for client entity
}
public ClientDTO (Societe societe) {
this (societe);
// set societe fields.
}
// other constructors.
}
You can modify your getClientByID method to accept an additional argument which will say what type of entity your want to retrieve and get back:
public class ClientDaoImpl implements ClientDao {
#PersistenceContext
private EntityManager em;
public <T extends Client> T getByID(int id_client, Class<T> klass) {
return em.find(klass, id_client);
}
}
And you can use this dao in the following manner:
Societe societe = clientDao.getByID(42, Societe.class);
Particulier particulier = clientDao.getByID(43, Particulier.class);
I'm developing a Java EE application with persistence.
My Car class has a few Reservation and the Reservation class extends the Quote class.
For some reason Reservation is not an Entity class. My guess is that there is something wrong with the inheritance but I can't seem to figure it out.
Car looks something like this:
#Entity
public class Car {
#Id
private int id;
#OneToOne(cascade=PERSIST, mappedBy="CarType")
private CarType type;
#OneToMany(cascade=REMOVE, mappedBy="Quote")
private Set<Reservation> reservations;
public Car() {}
.
.
.
public boolean equals(Object otherObject) {
...
}
public int hashCode() {
...
}
Reservation looks something like this
#Entity
#Table(name = "Reservation")
public class Reservation extends Quote {
#Id
#GeneratedValue(strategy=AUTO)
#Column(name="reservationId")
private int reservationId;
private int carId;
public Reservation() {}
public Reservation(Quote quote, int carId) {
super(quote.getCarRenter(), quote.getStartDate(), quote.getEndDate(),
quote.getRentalCompany(), quote.getCarType(), quote.getRentalPrice());
this.carId = carId;
}
.
.
.
public boolean equals(Object otherObject) {
...
}
public int hashCode() {
...
}
}
Quote looks something like this:
#MappedSuperclass
public class Quote implements Serializable {
#Id
#GeneratedValue(strategy=AUTO)
#Column(name="quoteId")
private int quoteId;
#Temporal(DATE)
private Date startDate;
#Temporal(DATE)
private Date endDate;
private String carRenter;
private String rentalCompany;
private String carType;
private double rentalPrice;
public Quote() {}
public Quote(String carRenter, Date start, Date end, String rentalCompany, String carType, double rentalPrice) {
...
}
.
.
.
#Override
public int hashCode() {
...
}
#Override
public boolean equals(Object obj) {
...
}
}
Why is Reservation not a correct Entity class?
The entity Reservation must delete the #id because daughter inherits it from super class
Designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it.
I solved the problem by doing the following things:
I removed the id from Reservation because it inherits it from Quote
I manually added Reservation to persistence.xml as an Entity
Consider the diagram:
I've been working with JPA for a short time and, so far, I have never had the need to persist extended classes... As you can see by the example, SNMPNode, IPNode, etc are all extended classes from Node that is also extended from GeoLocation.
I understand that I can annotate the master classes with #MappedSuperclass and IPNode and SNMPNode will inherit their properties for persisting... But in this scenario I will end up with nearly identical tables and, to my understanding, instead of doing that I could just group all information in Node and work with a single table.
Is this the way persistence of extended classes on JPA work or my concepts are wrong?
Same thing as a resumed piece of code:
public class Node extends GeoLocation {
private String name;
private Group group;
private Location location;
private Type type;
private Company company;
}
public class IPNode extends Node {
private Long id;
private String ipAddress;
}
public class SNMPNode extends Node {
private Long id;
private SNMPServer server;
}
[[ EDITED AFTER ANSWER FROM THIS POINT ]]
For the sake of contributing, here's a sample of what I end up doing:
INode:
public interface INode {
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
public String getIpAddress();
public void setIpAddress(String ipAddress);
public String getCommunity();
public void setCommunity(String community);
}
Node:
#Entity
#DiscriminatorValue("N")
#DiscriminatorColumn(name="NODE_TYPE",discriminatorType=DiscriminatorType.STRING, length=20)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Node extends GeoLocation implements INode {
#Id
#GeneratedValue
private Long id;
private String name;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
(... Overrides from INode ...)
}
IPNode:
#Entity
#DiscriminatorValue("I")
public class IPNode extends Node {
private String ipAddress;
public String getIpAddress() { return this.ipAddress;}
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
(... Overrides from INode ...)
}
SNMPNode:
#Entity
#DiscriminatorValue("S")
public class SNMPNode extends Node {
private String community;
public String getCommunity() { return community;}
public void setCommunity(String community) { this.community = community; }
(... Overrides from INode ...)
}
NodeRepository:
#Repository
public interface NodeRepository extends JpaRepository<Node, Long> { }
So now I can do stuff like this:
#ContextConfiguration("classpath:/spring/application-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class NodeRepositoryTest {
#Autowired
NodeRepository repo;
private INode node;
#Before
#Transactional
#Rollback(false)
public void setup() {
node = new IPNode();
node.setName("ipNode");
node.setIpAddress("1.1.1.1");
repo.save((IPNode)node);
node = new SNMPNode();
node.setName("snmpNode");
node.setIpAddress("2.2.2.2");
node.setCommunity("some text");
repo.save((SNMPNode)node);
}
#Test
#Transactional
public void Test() throws Exception {
INode testNode = repo.findOne(1L);
assertNotNull(testNode);
}
}
Both Node types are saved on the same table and so their keys can't repeat... My REST URL can grab them by /nodes/1 or /nodes/2, which was my main goal after all...
Thanks :)
If your base class is annotated with #MappedSuperclass then inheritance is only relevant in the OOP context. The #MappedSuperclass properties are simply copied to each sub-class associated database table and you can only query for sub-class entities.
Single table inheritance yields the best performance (no join or union is involved) at the price of not being able to declare not-nullable all specific sub-class properties (since all base and all sub-class specific properties go to a single database table).
With joined inheritance tables you can have the base class properties in a base database table, and each specific sub-class has it's own associated table. The sub-class table is linked with the base database table through a FK, so you need to join these tables to fetch a sub-class entity. As opossed to #MappedSuperclass, the base class is queryable since both the OOP context and the database reflect the inheritance model.
I want to map the following classes with Hibernate JPA:
My code looks like this:
#Entity
public class Grid{
#Id
#GeneratedValue
private Long id;
#Column(unique=true)
private String name;
private String location;
private BigDecimal costsPerCPUMinute;
#OneToMany(mappedBy="grid")
private List<Membership> mem;
public List<Membership> getMem() {
return mem;
}
public void setMem(List<Membership> mem) {
this.mem = mem;
}
#Entity
public class User extends Person{
#Column(nullable=false, unique=true)
private String username;
#Column(length=16,columnDefinition="BINARY(16)")
private byte[] password;
#OneToMany(mappedBy="user")
private List<Membership> mem;
public List<Membership> getMem() {
return mem;
}
public void setMem(List<Membership> mem) {
this.mem = mem;
}
#SuppressWarnings("serial")
#Entity
public class Membership implements Serializable{
private Date registration;
private Double discount;
#Id
#ManyToOne
private Grid grid;
#Id
#ManyToOne
private User user;
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Unfortunately, I get the following Exception:
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: dst1.model.Membership.grid in dst1.model.Grid.mem
As far as I understand the message, grid cannot be found in Membership. But as you can see in de code, there definitly is a variable named grid in the Membership class.
Does anybody has an idea whats going wrong?
Update: As suggested in the comments, I also tried to change the Membership Class by using IDClass or EmbeddedID. The EmbeddedID version looks like this:
#SuppressWarnings("serial")
#Entity
public class Membership implements Serializable{
private Date registration;
private Double discount;
#EmbeddedId
private MembershipPK membershipPK;
public Membership(){};
public MembershipPK getMembershipPK() {
return membershipPK;
}
public void setMembershipPK(MembershipPK membershipPK) {
this.membershipPK = membershipPK;
}
#SuppressWarnings("serial")
#Embeddable
public class MembershipPK implements Serializable{
#ManyToOne
private Grid grid;
#ManyToOne
private User user;
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Unfortunately, I still get the same exception.
Update 2: I will rewrite all three classes from scratch tomorrow evening and update this post if that changes anything.
You should be able to use something like
#Embeddable
public class MembershipId
{
protected Grid grid;
protected User user;
}
#Entity
public class Membership {
#EmbeddedId
MembershipId id;
}
#Entity
public class User {
#OneToMany(mappedBy="id.user")
private Set<Membership> memberships = new HashSet<Membership>();
}
From the top of my head: shouldn't this be rather
public class MembershipId implements Serializable {
private Grid grid;
private User user;
// immutable constructor, getters, equals, and hashCode
}
#Entity
#IdClass(MembershipId.class)
public class Membership implements Serializable {
#Id #ManyToOne
private Grid grid;
#Id #ManyToOne
private User user;
// rest of class
}
Edit: What the exception is telling you is that your Grid class has a field named mem and that the entity represented by this field needs a grid field, but doesn't have one. Here is where your Grid.mem needs a grid field:
#Entity
public class Grid{
...
#OneToMany(mappedBy="grid")
private List<Membership> mem;
This mapping can only work if there is a property grid in Membership.class. If you hide the grid inside the IdClass, Grid.mem can't find it. You might try this:
#Embeddable
public class MembershipId implements Serializable {
private Grid grid;
private User user;
// immutable constructor, getters, equals, and hashCode
}
#Entity
public class Membership implements Serializable {
#EmbeddedId
private MembershipId id;
#ManyToOne
private Grid grid;
#ManyToOne
private User user;
// rest of class
}