When using discriminator value for single table strategy, the first inserted entity's discriminator value is null but the value is there in database - jpa

When using discriminator value for inheritance/single table strategy, the first inserted entity's discriminator value is null but the value is there in the database.
I have to restart the server so that the query result containes the discriminator value:
package entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("Null")
#Table(name="ALLUSER")
#NamedQueries({
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
#NamedQuery(name = "User.findByAccount", query = "SELECT u FROM User u WHERE u.account = :account")
})
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String account;
private String password;
private String userType;
public User() {
super();
}
public User(String account, String password) {
super();
this.account = account;
this.password = password;
}
#Id
#Column(name = "account")
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "user_type", insertable = false, updatable = false, nullable = false)
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
#Override
public String toString() {
return account;
}
}
#Entity
#DiscriminatorValue("Normal")
#NamedQueries({
#NamedQuery(name = "NormalUser.findAll", query = "SELECT u FROM NormalUser u")
})
public class NormalUser extends User implements Serializable{
/**
*
*/
//private String account;
private static final long serialVersionUID = 1L;
private LinkedHashSet<Customer> customers;
public NormalUser() {
super();
}
#OneToMany(fetch=FetchType.EAGER, mappedBy="normalUser", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) //eager can be optimized when deleting a normal user
public LinkedHashSet<Customer> getCustomers() {
return customers;
}
public void setCustomers(LinkedHashSet<Customer> customers) {
this.customers = customers;
}
// #Column(name = "account")
// //have to override in order to get account to use
// public String getAccount() {
// return account;
// }
//
// public void setAccount(String account) {
// this.account = account;
// }
}
If I just add a new normal user(child entity), then query this user whose user type will be null:
I use eclipse-link as the JPA implementation and Java EE three-tiered web architecture.
I know this definitely has something to do with the working of entity manager and persistence but I don't know the details. I also don't know how to resolve it. Any suggestion is welcome!

You are not setting the 'type' field within your entities, and JPA doesn't set it for you - not in the java object anyway. If it isn't set when you persist an entity, it will remain unset for as long as that entity is cached (locally or the shared EMF level cache). Restarting the app works because it clears the cache, forcing any fetches of existing entities to load from the database, where the type was set based on the discriminator column value.
You can set the type when creating the class, or force the data to be reloaded from the database by calling em.refresh on the instance. In this case though, it seems strange to even bother mapping the type column as a basic mapping - the getType method should just return the static discriminator value for the class, and you cannot change the type string anyway.

Related

JPA won't save an entity correctly unless the references to other entities are set

If I save this entity using JPA repository with a new defaultAssetId, it will only update the defaultAsssetId to the new value if I set defaultAsset as well. I want to be able to save without setting defaultAsset.
Same problem applies to taskType and assetRole.
package au.com.polonious.conf.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
#Entity
public class TaskTypeAssetRole implements Serializable {
#GenericGenerator(name="tasktypeassetroleidseq",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "tasktypeassetroleidseq"),
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "1")
})
#Id
#GeneratedValue(generator = "tasktypeassetroleidseq")
private Long id;
#Column(insertable = false, updatable=false)
private Long taskTypeId;
#Fetch(FetchMode.JOIN)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="tasktypeid")
private TaskType taskType;
#Column(insertable = false, updatable=false)
private Long assetRoleId;
#Fetch(FetchMode.JOIN)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="assetRoleId")
private Role assetRole;
#Column(insertable = false, updatable=false)
private Long defaultAssetId;
#Fetch(FetchMode.JOIN)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="defaultassetid")
private Asset defaultAsset;
private Date startDate;
private Date endDate;
private String notes;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTaskTypeId() {
return taskTypeId;
}
public void setTaskTypeId(Long taskTypeId) {
this.taskTypeId = taskTypeId;
}
public TaskType getTaskType() {
return taskType;
}
public void setTaskType(TaskType taskType) {
this.taskType = taskType;
}
public Long getAssetRoleId() {
return assetRoleId;
}
public void setAssetRoleId(Long assetRoleId) {
this.assetRoleId = assetRoleId;
}
public Role getAssetRole() {
return assetRole;
}
public void setAssetRole(Role assetRole) {
this.assetRole = assetRole;
}
public Long getDefaultAssetId() {
return defaultAssetId;
}
public void setDefaultAssetId(Long defaultAssetId) {
this.defaultAssetId = defaultAssetId;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
}
I tried saving a taskTypeAssetRole with a new defaultAssedId without setting defaultAsset and I expected the defaultAssedId for that entry in the database to be updated.
What ended up happening was defaultAssetId didn't change although everything else in the entry did update successfully and there were no errors.
Your mapping is inherently broken. The column defaultassetiId is mapped to two different values: the field defaultAssetId and to the id of defaultAsset.
You should remove the defaultAssetId because this construct might break on any update of your JPA provider.
You can use references instead of full entities to set the reference values without loading entities from the database. See https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html#getReferenceById-ID-
If you don't follow this advice you should remove updatable=false if you want to update a field.

how to store PostgreSQL jsonb using SpringBoot + JPA?

I'm working on a migration software that will consume unknown data from REST services.
I already think about use MongoDB but I decide to not use it and use PostgreSQL.
After read this I'm trying to implement it in my SpringBoot app using Spring JPA but I don't know to map jsonb in my entity.
Tried this but understood nothing!
Here is where I am:
#Repository
#Transactional
public interface DnitRepository extends JpaRepository<Dnit, Long> {
#Query(value = "insert into dnit(id,data) VALUES (:id,:data)", nativeQuery = true)
void insertdata( #Param("id")Integer id,#Param("data") String data );
}
and ...
#RestController
public class TestController {
#Autowired
DnitRepository dnitRepository;
#RequestMapping(value = "/dnit", method = RequestMethod.GET)
public String testBig() {
dnitRepository.insertdata(2, someJsonDataAsString );
}
}
and the table:
CREATE TABLE public.dnit
(
id integer NOT NULL,
data jsonb,
CONSTRAINT dnit_pkey PRIMARY KEY (id)
)
How can I do this?
Note: I don't want/need an Entity to work on. My JSON will always be String but I need jsonb to query the DB
Tried this but understood nothing!
To fully work with jsonb in Spring Data JPA (Hibernate) project with Vlad Mihalcea's hibernate-types lib you should just do the following:
1) Add this lib to your project:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.2.2</version>
</dependency>
2) Then use its types in your entities, for example:
#Data
#NoArgsConstructor
#Entity
#Table(name = "parents")
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Parent implements Serializable {
#Id
#GeneratedValue(strategy = SEQUENCE)
private Integer id;
#Column(length = 32, nullable = false)
private String name;
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private List<Child> children;
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private Bio bio;
public Parent(String name, List children, Bio bio) {
this.name = name;
this.children = children;
this.bio = bio;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Child implements Serializable {
private String name;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Bio implements Serializable {
private String text;
}
Then you will be able to use, for example, a simple JpaRepository to work with your objects:
public interface ParentRepo extends JpaRepository<Parent, Integer> {
}
parentRepo.save(new Parent(
"parent1",
asList(new Child("child1"), new Child("child2")),
new Bio("bio1")
)
);
Parent result = parentRepo.findById(1);
List<Child> children = result.getChildren();
Bio bio = result.getBio();
You are making things overly complex by adding Spring Data JPA just to execute a simple insert statement. You aren't using any of the JPA features. Instead do the following
Replace spring-boot-starter-data-jpa with spring-boot-starter-jdbc
Remove your DnitRepository interface
Inject JdbcTemplate where you where injecting DnitRepository
Replace dnitRepository.insertdata(2, someJsonDataAsString ); with jdbcTemplate.executeUpdate("insert into dnit(id, data) VALUES (?,to_json(?))", id, data);
You were already using plain SQL (in a very convoluted way), if you need plain SQL (and don't have need for JPA) then just use SQL.
Ofcourse instead of directly injecting the JdbcTemplate into your controller you probably want to hide that logic/complexity in a repository or service.
There are already several answers and I am pretty sure they work for several cases. I don't wanted to use any more dependencies I don't know, so I look for another solution.
The important parts are the AttributeConverter it maps the jsonb from the db to your object and the other way around. So you have to annotate the property of the jsonb column in your entity with #Convert and link your AttributeConverter and add #Column(columnDefinition = "jsonb") as well, so JPA knows what type this is in the DB. This should already make it possible to start the spring boot application. But you will have issues, whenever you try to save() with the JpaRepository. I received the message:
PSQLException: ERROR: column "myColumn" is of type jsonb but
expression is of type character varying.
Hint: You will need to rewrite or cast the expression.
This happens because postgres takes the types a little to serious.
You can fix this by a change in your conifg:
datasource.hikari.data-source-properties: stringtype=unspecified
datasource.tomcat.connection-properties: stringtype=unspecified
Afterwards it worked for me like a charm, and here is a minimal example.
I use JpaRepositories:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {
}
The Entity:
import javax.persistence.Column;
import javax.persistence.Convert;
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
#Convert(converter = MyConverter.class)
#Column(columnDefinition = "jsonb")
private MyJsonObject jsonContent;
}
The model for the json:
public class MyJsonObject {
protected String name;
protected int age;
}
The converter, I use Gson here, but you can map it however you like:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class MyConverter implements AttributeConverter<MyJsonObject, String> {
private final static Gson GSON = new Gson();
#Override
public String convertToDatabaseColumn(MyJsonObject mjo) {
return GSON.toJson(mjo);
}
#Override
public MyJsonObject convertToEntityAttribute(String dbData) {
return GSON.fromJson(dbData, MyJsonObject.class);
}
}
SQL:
create table my_entity
(
id serial primary key,
json_content jsonb
);
And my application.yml (application.properties)
datasource:
hikari:
data-source-properties: stringtype=unspecified
tomcat:
connection-properties: stringtype=unspecified
For this case, I use the above tailored converter class, you are free to add it in your library. It is working with the EclipseLink JPA Provider.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;
import org.postgresql.util.PGobject;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;
#Converter
public final class PgJsonbToMapConverter implements AttributeConverter<Map<String, ? extends Object>, PGobject> {
private static final Logger LOGGER = Logger.getLogger(PgJsonbToMapConverter.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
#Override
public PGobject convertToDatabaseColumn(Map<String, ? extends Object> map) {
PGobject po = new PGobject();
po.setType("jsonb");
try {
po.setValue(map == null ? null : MAPPER.writeValueAsString(map));
} catch (SQLException | JsonProcessingException ex) {
LOGGER.error("Cannot convert JsonObject to PGobject.");
throw new IllegalStateException(ex);
}
return po;
}
#Override
public Map<String, ? extends Object> convertToEntityAttribute(PGobject dbData) {
if (dbData == null || dbData.getValue() == null) {
return null;
}
try {
return MAPPER.readValue(dbData.getValue(), new TypeReference<Map<String, Object>>() {
});
} catch (IOException ex) {
LOGGER.error("Cannot convert JsonObject to PGobject.");
return null;
}
}
}
Usage example, for an entity named Customer.
#Entity
#Table(schema = "web", name = "customer")
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Convert(converter = PgJsonbToMapConverter.class)
private Map<String, String> info;
public Customer() {
this.id = null;
this.info = null;
}
// Getters and setter omitted.
If you're using R2DBC you can use dependency io.r2dbc:r2dbc-postgresql, and use type io.r2dbc.postgresql.codec.Json in your member attributes of an entity class, e.g.:
public class Rule {
#Id
private String client_id;
private String username;
private String password;
private Json publish_acl;
private Json subscribe_acl;
}

playframework JPA Error and DB design issue

I get the following error:
JPA error
A JPA error occurred (Unable to build EntityManagerFactory): #OneToOne or #ManyToOne on models.Issue.project references an unknown entity: models.Project
Here you can see my entities:
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
import models.Issue;
import models.Component;
public class Project extends Model{
public String self;
#Id
public String key;
#OneToMany (mappedBy="Project", cascade=CascadeType.ALL)
public List<Component> components;
#OneToMany (mappedBy="Project", cascade=CascadeType.ALL)
public List<Issue> issues;
public Project(String self, String key) {
this.self = self;
this.key = key;
this.components = new ArrayList<Component>();
this.issues = new ArrayList<Issue>();
}
public Project addComponent(String self, int component_id, String name, int issuecount) {
Component newComponent = new Component(self, component_id, name, issuecount, this);
this.components.add(newComponent);
return this;
}
public Project addIssue(Date created, Date updated, String self, String key,
String type, Status status) {
Issue newIssue = new Issue(created, updated, self, key, type, status, this);
this.issues.add(newIssue);
return this;
}
}
and this is the other
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
import models.Project;
import models.Status;
import models.Component;
#Entity
public class Issue extends Model {
#Id
public String key;
public Date created;
public Date updated;
public String self;
public String type;
#ManyToOne
public Status status;
#ManyToOne
public Project project;
#OneToMany
public List<Component> components;
public Issue(Date created, Date updated, String self, String key,
String type, Status status, Project project ) {
this.created = created;
this.updated = updated;
this.self = self;
this.key = key;
this.status = status;
this.type = type;
this.project=project;
this.components=new ArrayList<Component>();
}
public Issue addComponent(Component component) {
this.components.add(component);
return this;
}
}
I'm using Play 1.2.4 and Eclipse. Now my db is in mem.
I have also a second question. Ideally I need a db for each user and I want to delete the content of the tables every time the user logs in ( or logs out ) and populate the table again when the user logs in (this is because the information stored in my db must be in synch with service I'm connecting to ). How should I do?
I totally have no clue. Please help me.
public class Project extends Model
is missing the #Entity annotation
The "mappedBy" should reference the property in the other entity which is "project" and not "Project".

OneToOne Mapping - Play Framework Entity

I'm struggling to successfully implement a OneToOne mapping on my play framework application.
examples I have are:
#Entity
public class Profile extends GenericModel {
#Id
#GeneratedValue(generator = "foreignGenerator")
#GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "user"))
public static int id;
#OneToOne(mappedBy="profile", cascade = {CascadeType.ALL})
public static User user;
}
and :
#Entity
public class User extends Model {
#Required
public String firstName;
#Required
public String surname;
}
in this setup it throws:
org.hibernate.AnnotationException: No identifier specified for entity: models.Profile
EDIT: As per Christian Boariu's answer, I have modified Profile to what you have suggested and User to:
#Entity
public class User extends GenericModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long user_id;
#Required
public String firstName;
#Required
public String surname;
#OneToOne(cascade = {CascadeType.ALL})
#PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "profile_id")
public Profile profile;
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
}
Also added getter/setter to Profile:
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
But I am now getting hibernate.id.IdentifierGenerationException: null id generated for:class models.Profile.. not sure how to correct?
The problem is about your Id definition.
It should not be static.
Also, user should not be static as well.
UPDATE:
So your class should be like this:
#Entity
public class Profile extends GenericModel {
#Id
#GeneratedValue(generator = "foreignGenerator")
#GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "user"))
public int id;
#OneToOne(mappedBy="profile", cascade = {CascadeType.ALL})
public User user;
}
Fixed. suggesstions, as above fixed the #OneToOne issue and the hibernate.id.IdentifierGenerationException: null id generated for:class models.Profile was due to trying to persist an entity with a null id - due to using #primaryKeyJoin, so changed to #JoinColumn

Netbeans code for Postgresql and Eclipselink - identifying relation question

I have the following database tables:
party, with a pk "pty_id" connected to a sequence for generating pk values.
person with a fpk "prs_pty_id" in an identifying relation to party.pty_id.
company ... which is not involved at the moment, but obviously this is kindof sub-superclass setup, and it could probably have been implemented with the subclassing mechanism in postgresql, but that's for another day.
So, I use Netbeans 6.9.1 to generate JPA entity classes and controller/dao code to handle this. It works just nice, I only have to add one annotation to the Party Entity bean: #GeneratedValue(strategy = GenerationType.IDENTITY). This is not needed for the Person entity bean, because it should always have the pk value of the Party that it is connected to.
So here is what I do to create a person:
PartyJpaController parController = new PartyJpaController();
PersonJpaController perController = new PersonJpaController();
Party par = new Party();
Person per = new Person();
par.setComment("jalla");
per.setName("Per Vers");
parController.create(par);
per.setPrsPtyId(par.getPtyId()); // <== why do I need to set this ...
Long partyId = par.getPtyId();
par.setPerson(per); // <== ... when this explicitly expresses the relationship?
perController.create(per);
parController.edit(par);
Party foundParty = parController.findParty(partyId);
Person foundPerson = foundParty.getPerson();
System.err.println(foundPerson.getName());
This works just fine. But why do I have to explicitly set the pk of the Person bean? It is in an identifying relationship with the Party. If I skip it, I get
java.lang.IllegalArgumentException: An instance of a null PK has been incorrectly provided for this find operation.
in perController.create(per), which is code generated by Netbeans:
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
Party party = person.getParty();
if (party != null) {
party = em.getReference(party.getClass(), party.getPtyId()); // <== Exception thrown here
person.setParty(party);
}
em.persist(person);
if (party != null) {
party.setPerson(person);
party = em.merge(party);
}
em.getTransaction().commit();
So, I suppose the Netbeans-generated code is not quite tuned for identifying relationships? What's the best way to code this?
Software used: Eclipselink version 2.1.1 Postgresql 8.4 Netbeans 6.9.1 Java/JDK 1.6.0_21
Here are my beans, they're generated by netbeans 6.9.1 from schema, except the #GeneratedValue(strategy = GenerationType.IDENTITY) in Party, which I've added in order to use the serial/sequence pk generation in postgresql.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.martinsolaas.webmarin.jpa;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
/**
*
* #author jms
*/
#Entity
#Table(name = "person", catalog = "webmarin", schema = "webmarin")
#NamedQueries({
#NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p"),
#NamedQuery(name = "Person.findByPrsPtyId", query = "SELECT p FROM Person p WHERE p.prsPtyId = :prsPtyId"),
#NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.name = :name"),
#NamedQuery(name = "Person.findByCellphone", query = "SELECT p FROM Person p WHERE p.cellphone = :cellphone"),
#NamedQuery(name = "Person.findByOfficephone", query = "SELECT p FROM Person p WHERE p.officephone = :officephone")})
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "prs_pty_id", nullable = false)
#MapsId
private Long prsPtyId;
#Column(name = "name", length = 255)
private String name;
#Column(name = "cellphone", length = 55)
private String cellphone;
#Column(name = "officephone", length = 55)
private String officephone;
#JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id", nullable = false, insertable = false, updatable = false)
#OneToOne(optional = false)
private Party party;
public Person() {
}
public Person(Long prsPtyId) {
this.prsPtyId = prsPtyId;
}
public Long getPrsPtyId() {
return prsPtyId;
}
public void setPrsPtyId(Long prsPtyId) {
this.prsPtyId = prsPtyId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCellphone() {
return cellphone;
}
public void setCellphone(String cellphone) {
this.cellphone = cellphone;
}
public String getOfficephone() {
return officephone;
}
public void setOfficephone(String officephone) {
this.officephone = officephone;
}
public Party getParty() {
return party;
}
public void setParty(Party party) {
this.party = party;
}
#Override
public int hashCode() {
int hash = 0;
hash += (prsPtyId != null ? prsPtyId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Person)) {
return false;
}
Person other = (Person) object;
if ((this.prsPtyId == null && other.prsPtyId != null) || (this.prsPtyId != null && !this.prsPtyId.equals(other.prsPtyId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.martinsolaas.webmarin.jpa.Person[prsPtyId=" + prsPtyId + "]";
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.martinsolaas.webmarin.jpa;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapsId;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
/**
*
* #author jms
*/
#Entity
#Table(name = "party", catalog = "webmarin", schema = "webmarin")
#NamedQueries({
#NamedQuery(name = "Party.findAll", query = "SELECT p FROM Party p"),
#NamedQuery(name = "Party.findByPtyId", query = "SELECT p FROM Party p WHERE p.ptyId = :ptyId"),
#NamedQuery(name = "Party.findByComment", query = "SELECT p FROM Party p WHERE p.comment = :comment")})
public class Party implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "pty_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long ptyId;
#Basic(optional = false)
#Column(name = "comment", nullable = false, length = 2147483647)
private String comment;
#JoinTable(name = "party_relationship", joinColumns = {
#JoinColumn(name = "parent_pty_id", referencedColumnName = "pty_id", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "child_pty_id", referencedColumnName = "pty_id", nullable = false)})
#ManyToMany
private List partyList;
#ManyToMany(mappedBy = "partyList")
private List partyList1;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "party")
private Person person;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "party")
private Company company;
public Party() {
}
public Party(Long ptyId) {
this.ptyId = ptyId;
}
public Party(Long ptyId, String comment) {
this.ptyId = ptyId;
this.comment = comment;
}
public Long getPtyId() {
return ptyId;
}
public void setPtyId(Long ptyId) {
this.ptyId = ptyId;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public List getPartyList() {
return partyList;
}
public void setPartyList(List partyList) {
this.partyList = partyList;
}
public List getPartyList1() {
return partyList1;
}
public void setPartyList1(List partyList1) {
this.partyList1 = partyList1;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
#Override
public int hashCode() {
int hash = 0;
hash += (ptyId != null ? ptyId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Party)) {
return false;
}
Party other = (Party) object;
if ((this.ptyId == null && other.ptyId != null) || (this.ptyId != null && !this.ptyId.equals(other.ptyId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.martinsolaas.webmarin.jpa.Party[ptyId=" + ptyId + "]";
}
}
Eventually, here is the schema SQL
CREATE SEQUENCE webmarin.party_pty_id_seq;
CREATE TABLE webmarin.party (
pty_id BIGINT NOT NULL DEFAULT nextval('webmarin.party_pty_id_seq'),
comment TEXT NOT NULL,
CONSTRAINT pty_pk PRIMARY KEY (pty_id)
);
ALTER SEQUENCE webmarin.party_pty_id_seq OWNED BY webmarin.party.pty_id;
CREATE TABLE webmarin.company (
cmp_pty_id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
CONSTRAINT cmp_pk PRIMARY KEY (cmp_pty_id)
);
CREATE TABLE webmarin.party_relationship (
parent_pty_id BIGINT NOT NULL,
child_pty_id BIGINT NOT NULL,
CONSTRAINT ptr_pk PRIMARY KEY (parent_pty_id, child_pty_id)
);
CREATE TABLE webmarin.person (
prs_pty_id BIGINT NOT NULL,
name VARCHAR(255),
cellphone VARCHAR(55),
officephone VARCHAR(55),
CONSTRAINT prs_pk PRIMARY KEY (prs_pty_id)
);
ALTER TABLE webmarin.party_relationship ADD CONSTRAINT parent_party_party_relationship_fk
FOREIGN KEY (parent_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
ALTER TABLE webmarin.party_relationship ADD CONSTRAINT child_party_party_relationship_fk
FOREIGN KEY (child_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
ALTER TABLE webmarin.person ADD CONSTRAINT party_person_fk
FOREIGN KEY (prs_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
ALTER TABLE webmarin.company ADD CONSTRAINT party_company_fk
FOREIGN KEY (cmp_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
JPA 2.0 improved considerably the support of derived identifiers with the Id and MapsId annotations (they should be preferred now) that you can use on XxxToOne associations. Below one of the example taken from the specification:
2.4.1.3 Examples of Derived Identities
...
Example 4:
The parent entity has a simple primary
key:
#Entity
public class Person {
#Id String ssn;
...
}
Case (a): The dependent entity has a single primary key attribute which
is mapped by the relationship
attribute. The primary key of
MedicalHistory is of type String.
#Entity
public class MedicalHistory {
// default join column name is overridden
#Id
#OneToOne
#JoinColumn(name="FK")
Person patient;
...
}
Sample query:
SELECT m
FROM MedicalHistory m
WHERE m.patient.ssn = '123-45-6789'
Case (b): The dependent entity has a single primary key attribute
corresponding to the relationship
attribute. The primary key attribute
is of the same basic type as the
primary key of the parent entity. The
MapsId annotation applied to the
relationship attribute indicates that
the primary key is mapped by the
relationship attribute.
#Entity
public class MedicalHistory {
#Id String id; // overriding not allowed
...
// default join column name is overridden
#MapsId
#JoinColumn(name="FK")
#OneToOne Person patient;
...
}
Sample query:
SELECT m
FROM MedicalHistory m WHERE m.patient.ssn = '123-45-6789'
I think that the MapsId might be what you're looking for.
Follow-up: Instead of this:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "prs_pty_id", nullable = false)
#MapsId
private Long prsPtyId;
#Column(name = "name", length = 255)
private String name;
#Column(name = "cellphone", length = 55)
private String cellphone;
#Column(name = "officephone", length = 55)
private String officephone;
#JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id", nullable = false, insertable = false, updatable = false)
#OneToOne(optional = false)
private Party party;
...
}
Try this:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "prs_pty_id", nullable = false)
private Long prsPtyId;
#Column(name = "name", length = 255)
private String name;
#Column(name = "cellphone", length = 55)
private String cellphone;
#Column(name = "officephone", length = 55)
private String officephone;
#MapsId
#JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id")
#OneToOne
private Party party;
...
}
References
JPA 2.0 specification
Section 2.4.1 "Primary Keys Corresponding to Derived Identities"
Section 2.4.1.1 "Specification of Derived Identities"
Section 2.4.1.2 "Mapping of Derived Identities"
Section 2.4.1.3 "Examples of Derived Identities"
JPA Wikibook
3.2 Primary Keys through OneToOne Relationships
3.2.2 JPA 2.0