HIbernate SELECT query for composite key defined using #IdClass annotation - java

My table VENDORBRANCH has composite keys: "vendorCode" and "vendorBranchCode" which I have defined using the #Id annotation and using #IdClass. The field "vendorCode" is referenced as a foreign key in VENDORCRTERMS class. I'm using postgresql db.
Right now my sql query in the service implimentation looks like this but i want to include composite keys in the query:
Query<?> query = session.createQuery("from VENDORBRANCH where vendorCode = ?");
query.setParameter(0, mf01_vendorCode);
I'm very new to hibernate so tried a few options for the select query but I'm not sure if it's correct to do it this way. So, what would be the best select statement to use for a composite key??
VENDORBRANCH class:
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import com.parkson.poMain.backend.data.VENDORBRANCH.VBpk;
#SuppressWarnings("serial")
#Entity
#IdClass(VBpk.class)
public class VENDORBRANCH implements Serializable {
#Id
private String vendorCode;
#Id
private String vendorBranchCode;
//getters and setters
// inner class defined for primary key(composite keys)
public static class VBpk implements Serializable {
protected String vendorCode;
protected String vendorBranchCode;
public String getvendorCode() {
return vendorCode;
}
public void vendorCode(String vendorCode) {
this.vendorCode = vendorCode;
}
public String vendorBranchCode() {
return vendorBranchCode;
}
public void vendorBranchCode(String vendorBranchCode) {
this.vendorBranchCode = vendorBranchCode;
}
public VBpk(){}
public VBpk(String vendorCode,String vendorBranchCode){
this.vendorCode = vendorCode;
this.vendorBranchCode = vendorBranchCode;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((vendorBranchCode == null) ? 0 : vendorBranchCode.hashCode());
result = prime * result + ((vendorCode == null) ? 0 : vendorCode.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
VBpk other = (VBpk) obj;
if (vendorBranchCode == null) {
if (other.vendorBranchCode != null)
return false;
} else if (!vendorBranchCode.equals(other.vendorBranchCode))
return false;
if (vendorCode == null) {
if (other.vendorCode != null)
return false;
} else if (!vendorCode.equals(other.vendorCode))
return false;
return true;
}
}
}
My other class: VENDORCRTERMS
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#SuppressWarnings("serial")
#Entity
public class VENDORCRTERMS implements Serializable {
#Id
private String vcrId ;
//This is the foreign key referenced from **VENDORBRANCH class**
#ManyToOne
#JoinColumns( {
#JoinColumn(name="vendorbranch_vendorcode", nullable = false),
#JoinColumn(name="vendorbranch_vendorBranchCode", nullable = false)} )
private VENDORBRANCH vendorbranch_vendorcode = new VENDORBRANCH();
// foreign key referenced from a different class
#ManyToOne
#JoinColumn(name= "creditterms_credittermscode" , nullable = false)
private CREDITTERMS creditterms_credittermscode = new CREDITTERMS();
//getters and setters
}

Related

ERROR: constraint for relation already exists with hibernate 4.3.1

I have this error when running the dll generated the hibernate
Failed to execute: alter table sezioniastratte add constraint FK_5nekcygup70my0ixo073o215d foreign key (padre_id) references sezioni
org.postgresql.util.PSQLException: ERROR: constraint "fk_5nekcygup70my0ixo073o215d" for relation "sezioniastratte" already exists
I'm mapping a complex structure, in particular, a structure with the composite pattern, this is the class diagram:
This is my code of the classes
package it.unibas.webanalytics.modello;
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.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
/**
*
* #author Vincenzo Palazzo
*/
#Entity(name = "sezioniastratte")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class AbstractSezione implements ISezione {
private Long id;
protected String identificativo;
private AbstractSezione root;
private AbstractSezione padre;
public AbstractSezione(String identificativo) {
this.identificativo = identificativo;
}
public AbstractSezione() {
}
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(unique = true, nullable = false, length = 150)
public String getIdentificativo() {
return identificativo;
}
public void setIdentificativo(String identificativo) {
this.identificativo = identificativo;
}
#OneToOne
public AbstractSezione getRoot() {
return root;
}
public void setRoot(AbstractSezione root) {
this.root = root;
}
#ManyToOne(cascade = CascadeType.ALL)
public AbstractSezione getPadre() {
return padre;
}
public void setPadre(AbstractSezione padre) {
this.padre = padre;
}
}
package it.unibas.webanalytics.modello;
import it.unibas.webanalytics.modello.visite.IVisitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
/**
*
* #author Vincenzo Palazzo
*/
#Entity(name = "pagine")
public class Pagina extends AbstractSezione{
#Deprecated
private String uuid;
private List<Visualizzazione> visualizzazioni = new ArrayList<>();
public Pagina(String identificativo) {
super(identificativo);
uuid = UUID.randomUUID().toString();
}
public Pagina() {
}
#OneToMany(mappedBy = "pagina", orphanRemoval = true)
public List<Visualizzazione> getVisualizzazioni() {
return visualizzazioni;
}
public void addVisualizzazione(Visualizzazione visualizzazione){
visualizzazioni.add(visualizzazione);
}
#Override
public boolean isPage(){
return true;
}
#Override
public void accept(IVisitor visitor) {
visitor.visitaPagina(this);
}
#Transient
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public void setVisualizzazioni(List<Visualizzazione> visualizzazioni) {
this.visualizzazioni = visualizzazioni;
}
public int dimensione(){
return this.visualizzazioni.size();
}
#Override
public int hashCode() {
int hash = 5;
hash = 67 * hash + Objects.hashCode(super.identificativo);
hash = 67 * hash + Objects.hashCode(this.uuid);
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Pagina other = (Pagina) obj;
if (!Objects.equals(super.identificativo, other.identificativo)) {
return false;
}
if (!Objects.equals(this.uuid, other.uuid)) {
return false;
}
return true;
}
}
package it.unibas.webanalytics.modello;
import it.unibas.webanalytics.modello.visite.IVisitor;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
/**
*
* #author Vincenzo Palazzo
*/
#Entity(name = "sezioni")
public class Sezione extends AbstractSezione {
private List<AbstractSezione> sezioniList = new ArrayList<>();
public Sezione() {
}
public Sezione(String identificativo) {
super(identificativo);
}
public void addSezione(AbstractSezione sezione) {
sezioniList.add(sezione);
}
#OneToMany(mappedBy = "padre")
public List<AbstractSezione> getSezioniList() {
return sezioniList;
}
public void setSezioniList(List<AbstractSezione> sezioniList) {
this.sezioniList = sezioniList;
}
#Override
public boolean isPage() {
return false;
}
#Override
public void accept(IVisitor visitor) {
visitor.visitaSezione(this);
}
}
package it.unibas.webanalytics.modello;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
/**
*
* #author Vincenzo Palazzo
*/
#Entity(name = "portali")
public class PortaleWeb {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
#Column(nullable = false, unique = true, length = 50)
private String nomeDominio;
#Column(nullable = false, unique = true, length = 50)
private String indirizzoIp;
#Column(length = 20)
private String indestatario;
#OneToOne(mappedBy = "root")
private AbstractSezione sezione;
public PortaleWeb() {
}
public PortaleWeb(String nomeDominio, String indirizzoIp, String indestatario, AbstractSezione sezione) {
this.nomeDominio = nomeDominio;
this.indirizzoIp = indirizzoIp;
this.indestatario = indestatario;
this.sezione = sezione;
}
public String getNomeDominio() {
return nomeDominio;
}
public String getIndirizzoIp() {
return indirizzoIp;
}
public String getIndestatario() {
return indestatario;
}
public ISezione getSezione() {
return sezione;
}
public void setNomeDominio(String nomeDominio) {
this.nomeDominio = nomeDominio;
}
public void setIndirizzoIp(String indirizzoIp) {
this.indirizzoIp = indirizzoIp;
}
public void setIndestatario(String indestatario) {
this.indestatario = indestatario;
}
public void setSezione(AbstractSezione sezione) {
this.sezione = sezione;
}
}
package it.unibas.webanalytics.modello;
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.ManyToOne;
/**
*
* #author Vincenzo Palazzo
*/
#Entity(name = "visualizzazioni")
public class Visualizzazione {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
#Column(nullable = true, length = 50)
private String nazioneDiProvenienza;
#Column(length = 250)
private String urlProvenienza;
#Column(length = 250)
private String urlDestinazione;
#Column(length = 50)
private String browser;
private int daQuanto;
#ManyToOne(cascade = CascadeType.ALL)
private Pagina pagina;
public Visualizzazione() {
}
public Visualizzazione(String nazioneDiProvenienza, String urlProvenienza, String urlDestinazione, String browser, int daQuanto) {
this.nazioneDiProvenienza = nazioneDiProvenienza;
this.urlProvenienza = urlProvenienza;
this.urlDestinazione = urlDestinazione;
this.browser = browser;
this.daQuanto = daQuanto;
}
public String getNazioneDiProvenienza() {
return nazioneDiProvenienza;
}
public String getUrlProvenienza() {
return urlProvenienza;
}
public String getUrlDestinazione() {
return urlDestinazione;
}
public String getBrowser() {
return browser;
}
public int getDaQuanto() {
return daQuanto;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Pagina getPagina() {
return pagina;
}
public void setPagina(Pagina pagina) {
this.pagina = pagina;
}
public void setNazioneDiProvenienza(String nazioneDiProvenienza) {
this.nazioneDiProvenienza = nazioneDiProvenienza;
}
public void setUrlProvenienza(String urlProvenienza) {
this.urlProvenienza = urlProvenienza;
}
public void setUrlDestinazione(String urlDestinazione) {
this.urlDestinazione = urlDestinazione;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public void setDaQuanto(int daQuanto) {
this.daQuanto = daQuanto;
}
}
This is the DLL generate by hibernate
create table pagine (
page boolean not null,
id int8 not null,
primary key (id)
);
create table portali (
id int8 not null,
indestatario varchar(20),
indirizzoIp varchar(50) not null,
nomeDominio varchar(50) not null,
primary key (id)
);
create table sezioni (
page boolean not null,
id int8 not null,
primary key (id)
);
create table sezioniastratte (
id int8 not null,
identificativo varchar(150) not null,
padre_id int8,
root_id int8,
primary key (id)
);
create table utente (
id int8 not null,
attivo boolean not null,
lastLogin timestamp,
nome varchar(255),
nomeUtente varchar(255),
password varchar(255),
ruolo varchar(255),
primary key (id)
);
create table visualizzazioni (
id int8 not null,
browser varchar(50),
daQuanto int4 not null,
nazioneDiProvenienza varchar(50),
urlDestinazione varchar(250),
urlProvenienza varchar(250),
pagina_id int8,
primary key (id)
);
alter table portali
add constraint UK_7h0fmoqq22xrlcvgnrybiafgb unique (indirizzoIp);
alter table portali
add constraint UK_jywg6ldg7jvgfjgaw9u81bfp8 unique (nomeDominio);
alter table sezioniastratte
add constraint UK_b0jnspivif39dgey09negpjjp unique (identificativo);
alter table utente
add constraint UK_7hipuu05v6vcqr7wbl8q7p4t2 unique (nomeUtente);
alter table pagine
add constraint FK_tijtvrfbi6n06ocq6fpffgdy9
foreign key (id)
references sezioniastratte;
alter table sezioni
add constraint FK_t88ok4wbwbhdtipul9l11xued
foreign key (id)
references sezioniastratte;
alter table sezioniastratte
add constraint FK_5nekcygup70my0ixo073o215d
foreign key (padre_id)
references sezioniastratte;
alter table sezioniastratte
add constraint FK_d0cntmtmcp2wvl6hqr8ddgg8w
foreign key (root_id)
references sezioniastratte;
alter table sezioniastratte
add constraint FK_5nekcygup70my0ixo073o215d
foreign key (padre_id)
references sezioni;
alter table visualizzazioni
add constraint FK_dblrq69ykw1wch2pn845p4xf7
foreign key (pagina_id)
references pagine;
create table hibernate_sequences (
sequence_name varchar(255),
sequence_next_hi_value int4
);
Solution
Sorry for the class diagram wrong but not have another example also sorry for my terrible English but I'm learning
You mixed with the realtionships on the hierarchy levels.
Either the list must be in the AbstractSezione or the padre must be in the Sezione.

Modeling composite primary key with sequence field

I have a table where we have orderId, locationId and sequenceNum columns apart from other columns. These three columns are part of primary key constraint. sequenceNum column increments for an orderId/locationId combination and is restarts from 1 for next orderId/locationId. So sequenceNum generation should be based on max(sequenceNum)+1 for orderId/locationId combination. Can this be modeled with JPA #Embeddable and #EmbeddedId? How to reset sequence back to 1 for orderId/locationId combination?
If you user hibernate, you could user this example. If not, find the sequence generator on the API you are using, and use the same logic.
Identifiable.java
package my.app.hibernate;
import java.io.Serializable;
public interface Identifiable<T extends Serializable> {
T getId();
}
CompositeKeyEntity.java
package my.app.hibernate;
import java.io.Serializable;
public interface CompositeKeyEntity<T extends Serializable> extends Identifiable<T> {
}
SingleKeyEntity.java
package my.app.hibernate;
import java.io.Serializable;
public interface SingleKeyEntity<T extends Serializable> extends Identifiable<T> {
}
AssignedIdentityGenerator.java
package my.app.hibernate;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.internal.CriteriaImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.util.FieldUtils;
public class AssignedIdentityGenerator extends IdentityGenerator {
private static final String ID_FIELD_NAME = "id";
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
private Field sequenceField;
private String entityClassName;
#Override
public Serializable generate(SessionImplementor session, Object obj) {
#SuppressWarnings("unchecked")
Identifiable<Serializable> identifiable = (Identifiable<Serializable>)obj;
entityClassName = obj.getClass().getName();
Criteria criteria = new CriteriaImpl(entityClassName, session);
criteria.setReadOnly(true);
Object toSet = null;
if (identifiable instanceof CompositeKeyEntity) {
Serializable id = identifiable.getId();
if (id != null) {
String embaddebleClassName = id.getClass().getName();
buildCriteriaForEmbeddedId(id, embaddebleClassName, criteria);
toSet = id;
}
} else if (obj instanceof SingleKeyEntity) {
toSet = identifiable;
sequenceField = FieldUtils.getField(identifiable.getClass(), ID_FIELD_NAME);
buildCriteriaForSingleId(criteria);
}
Number one = castToSequenceNumberType(1L);
Number value = (Number) criteria.uniqueResult();
if(value != null) {
value = castToSequenceNumberType(value.longValue() + one.longValue());
setFieldValue(sequenceField, value, toSet);
} else {
value = one;
setFieldValue(sequenceField, value, toSet);
}
return identifiable.getId();
}
private void buildCriteriaForSingleId(Criteria criteria) {
criteria.setProjection(Projections.max(ID_FIELD_NAME).as("seq"));
}
private void buildCriteriaForEmbeddedId(Serializable id, String embaddebleClassName, Criteria criteria) {
List<Field> fields = Arrays.asList(id.getClass().getDeclaredFields());
class Utils {
Field field;
boolean numberFound = false;
}
final Utils utils = new Utils();
for (Field field : fields) {
if ("serialVersionUID".equals(field.getName()) || "$jacocoData".equals(field.getName())) {
continue;
}
if (Number.class.isAssignableFrom(field.getType())) {
if (utils.numberFound) {
throw new IllegalArgumentException(
embaddebleClassName + " has more then one sequence field: " + field.getName() + ", "
+ utils.field.getName() + ",...");
}
utils.numberFound = true;
utils.field = field;
sequenceField = field;
criteria.setProjection(Projections.max(ID_FIELD_NAME + "." + sequenceField.getName()).as("seq"));
} else {
criteria.add(Restrictions.eq(ID_FIELD_NAME + "." + field.getName(), getFieldValue(field, id)));
}
}
}
private Number castToSequenceNumberType(Number n) {
return (Number) sequenceField.getType().cast(n);
}
private void setFieldValue(Field field, Object value, Object to) {
try {
field.setAccessible(true);
field.set(to, value);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOG.error(e.getMessage(), e);
}
}
private Object getFieldValue(Field field, Object from) {
try {
field.setAccessible(true);
return field.get(from);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOG.error(e.getMessage(), e);
}
return null;
}
}
Customer.java
package my.app.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import my.app.hibernate.SingleKeyEntity;
#Entity(name = "whatever_entity_name")
#GenericGenerator(name = "WHATEVER_NAMED_GENERATOR", strategy = "my.app.hibernate.AssignedIdentityGenerator")
public class Customer implements SingleKeyEntity<Long> {
#Id
#GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
private Long id;
#Column(nullable = false)
private String name;
}
CustomerItemsId.java (Item.java ommited as it follows SingleKeyEntity example)
package my.app.entities;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#Embeddable
public class CustomerItemsId implements Serializable {
private static final long serialVersionUID = 1L; //generate one
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
#ManyToOne
#JoinColumn(name = "item_id")
private Item item;
private Long seq; //name as you wish
}
CustomerItems.java
package my.app.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import my.app.hibernate.CompositeKeyEntity;
#Entity(name = "whatever_entity_name")
#GenericGenerator(name = "WHATEVER_NAMED_GENERATOR", strategy = "my.app.hibernate.AssignedIdentityGenerator")
public class CustomerItems implements CompositeKeyEntity<CustomerItemsId> {
#GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
private CustomerItems id;
#Column(nullable = false)
private String randomColumn1;
#Column(nullable = false)
private String randomColumn2;
}

how does ".merge(entity)" work?

i'm creating a java EE (web) application using JPA and EJB for model-tier.
i think i have to use Session Beans for CRUD.
this is my BrandFacade.java (session bean)
package model.business;
import model.localinterface.BrandFacadeLocal;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import model.entities.Brand;
#Stateless
public class BrandFacade extends AbstractFacade<Brand> implements BrandFacadeLocal, BrandFacadeRemote {
#PersistenceContext(unitName = "MyWheelEE-ejbPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public BrandFacade() {
super(Brand.class);
}
#Override
public boolean CreateBrand(String name) {
Brand brand=new Brand(0, name);
boolean result=true;
try {
em.persist(brand);
} catch (Exception e) {
result=false;
}
em.close();
return result;
}
#Override
public void deleteBrand(int brandOid) {
em.remove(getBrandByOid(brandOid));
em.flush();
}
#Override
public Brand getBrandByOid(int brandOid) {
em.flush();
return em.find(Brand.class, brandOid);
}
#Override
public void editBrand(Brand brand) {
em.merge(brand);
em.flush();
}
}
and this is my Brand.java class (entity)
package model.entities;
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.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#Entity
#Table(name = "brand")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Brand.findAll", query = "SELECT b FROM Brand b"),
#NamedQuery(name = "Brand.findByOid", query = "SELECT b FROM Brand b WHERE b.oid = :oid"),
#NamedQuery(name = "Brand.findByName", query = "SELECT b FROM Brand b WHERE b.name = :name")})
public class Brand implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "oid")
private Integer oid;
#Basic(optional = false)
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "brandOid")
private List<Wheelchair> wheelchairList;
public Brand() {
}
public Brand(Integer oid) {
this.oid = oid;
}
public Brand(Integer oid, String name) {
this.oid = oid;
this.name = name;
}
public Integer getOid() {
return oid;
}
public void setOid(Integer oid) {
this.oid = oid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlTransient
public List<Wheelchair> getWheelchairList() {
return wheelchairList;
}
public void setWheelchairList(List<Wheelchair> wheelchairList) {
this.wheelchairList = wheelchairList;
}
#Override
public int hashCode() {
int hash = 0;
hash += (oid != null ? oid.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 Brand)) {
return false;
}
Brand other = (Brand) object;
if ((this.oid == null && other.oid != null) || (this.oid != null && !this.oid.equals(other.oid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "model.entities.Brand[ oid=" + oid + " ]";
}
}
i wish to know how does .merge method work... i think it search in the DB the entity which has the primary key of the entity passed and then it works on edited fields right?
but how i can edit a brand knowing only the name?
It's quite simple really, here your answers:
I wish to know how does .merge method work...
When you call merge method, JPA will verify if the field marked as primary key (#Id) is not null:
- IF YES: JPA will create a new record in your database
- IT NOT: JPA will update your record using the id field value, something like (UPDATE table_name ..... WHERE id=?)
So, you are right :)
but how i can edit a brand knowing only the name?
If you wanna edit a record knowing another field rather than Id field, you will have 2 options:
1. Write JPQL, something like:
UPDATE Person p SET p.lastName = 'New Last Name' WHERE p.name = 'his name'
Write a Native Query, in this case, you will write PLAIN SQL and the run it
In both cases, you will need to do something like:
Query query = em.createQuery or em.createNativeQuery
and then just execute it

Join table with column. How is possible?

Anybody to know something about this kind of queries:
#Entity
#Table(name="ACCOUNT")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#NamedQueries({
#NamedQuery(name = Account.GET_EXPIRATION_DATE, query="SELECT account.expirationDate FROM " +
"Domain domain JOIN domain.account WHERE domain.id = :domainId"),
#NamedQuery(name = Account.GET_BALANCE, query="SELECT account.balance FROM " +
"Domain domain JOIN domain.account WHERE domain.id = :domainId")
})
I dont understand what is this "Domain domain JOIN domain.account" we can join one table with another but it seams we join table with column .. ?
these are the related classes:
package com.smsoffice.admin;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import static javax.persistence.CascadeType.*;
import com.smsoffice.billing.Account;
import com.smsoffice.billing.PrepaidAccount;
#Entity
#Table(name="DOMAIN")
#NamedQueries({
#NamedQuery(name=Domain.GET_ALL_DOMAINS, query="SELECT d FROM Domain d ORDER BY d.name"),
#NamedQuery(name=Domain.GET_ACCOUNT, query="SELECT d.account FROM Domain d WHERE d.id = :id"),
#NamedQuery(name=Domain.GET_DOMAIN_BY_STATUS, query="SELECT d FROM Domain d WHERE d.enabled = :enabled ORDER BY d.name"),
#NamedQuery(name=Domain.GET_DOMAIN_BY_NAME, query="SELECT d FROM Domain d WHERE d.name = :name")
})
public class Domain implements Serializable {
private static final long serialVersionUID = 1L;
public final static String GET_ALL_DOMAINS = "Domain.getAllDomains";
public final static String GET_ACCOUNT = "Domain.getAccount";
public final static String GET_DOMAIN_BY_STATUS = "Domain.getAllDomainsByStatus";
public final static String GET_DOMAIN_BY_NAME = "Domain.getDomainByName";
public final static transient Domain ROOT = new Domain("ROOT");
public static Domain SYSTEM_DOMAIN = new Domain("SYSTEM");
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(unique = true, length = 96)
private String name;
//unique id of the service - one per domain
#GeneratedValue(strategy = GenerationType.AUTO)
private int serviceId;
//indicates whether the domain is enabled
private Boolean enabled = true;
//short code for the domain sms events
private int shortCode;
//prefix for parsing
private String prefix;
private String clientPrefix = "";
//bank account
#OneToOne(cascade = {PERSIST, REFRESH, REMOVE})
private Account account = new PrepaidAccount();
#OneToMany
private Set<User> users = new HashSet<User>();
public Domain() {}
public Domain(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getServiceId() {
return serviceId;
}
public void setServiceId(int serviceId) {
this.serviceId = serviceId;
}
public int getShortCode() {
return shortCode;
}
public void setShortCode(int shortCode) {
this.shortCode = shortCode;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Set<User> getUsers() {
return users;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Domain)) {
return false;
}
Domain domain = (Domain) obj;
return getName().toUpperCase().equals(domain.getName().toUpperCase());
}
#Override
public int hashCode() {
return getName().toUpperCase().hashCode();
}
#Override
public String toString() {
return "[" + name + "("+ account + ")]";
}
public int getId() {
return id;
}
public void setClientPrefix(String clientPrefix) {
this.clientPrefix = clientPrefix != null ? clientPrefix : "";
}
public String getClientPrefix() {
return clientPrefix;
}
}
and this one:
package com.smsoffice.billing;
import static javax.persistence.CascadeType.ALL;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name="ACCOUNT")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#NamedQueries({
#NamedQuery(name = Account.GET_EXPIRATION_DATE, query="SELECT account.expirationDate FROM " +
"Domain domain JOIN domain.account account WHERE domain.id = :domainId"),
#NamedQuery(name = Account.GET_BALANCE, query="SELECT account.balance FROM " +
"Domain domain JOIN domain.account account WHERE domain.id = :domainId")
})
public abstract class Account {
public static final MathContext MATH_CONTEXT = new MathContext(9, RoundingMode.HALF_UP);
public static final int SCALE = 3;
public static final int PRECISION = 9;
public static final String GET_BALANCE = "Account.getBalance";
public static final String GET_EXPIRATION_DATE = "Account.getExpirationDate";
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(precision = PRECISION, scale = SCALE)
protected BigDecimal balance = BigDecimal.ZERO;
#OneToOne(cascade = ALL)
protected Tariff tariff;
private Date activationDate = new Date();
private Date expirationDate;
public Account() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, 1);
setExpirationDate(calendar.getTime());
}
public BigDecimal getBalance() {
return balance;
}
public Tariff getTariff() {
return tariff;
}
public void setTariff(Tariff tariff) {
this.tariff = tariff;
}
void deposit(BigDecimal amount) {
balance = balance.add(amount).setScale(SCALE, MATH_CONTEXT.getRoundingMode());
}
abstract boolean hasCredit(int eventCount);
abstract void makePayment(int eventCount) throws PaymentException;
public int getId() {
return id;
}
public Date getActivationDate() {
return activationDate;
}
public void setActivationDate(Date activationDate) {
this.activationDate = normalizeActivationDate(activationDate);
}
void setExpirationDate(Date expirationDate) {
this.expirationDate = normalizeExpirationDate(expirationDate);
}
public Date getExpirationDate() {
return expirationDate;
}
public boolean isExpired() {
Date now = new Date();
return now.after(expirationDate);
}
#Override
public String toString() {
return "[balance: " + balance + "; tariff: " + (tariff != null ? tariff.getPrice() : "no tariff") + "; expiration date: " + expirationDate.toString() + "]";
}
public void setBalance(BigDecimal newBalance) {
this.balance = newBalance;
}
private static final Date normalizeActivationDate(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
private static final Date normalizeExpirationDate(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
}
Well, these are not really SQL queries, but rather JPQL queries - there is a very good tutorial describing this, along with very nice examples. Note that in JPQL you do not work directly with the tables, bur rather with entities of your domain model.
But to your question, let's take this query as an example:
SELECT account.balance FROM Domain domain JOIN domain.account WHERE domain.id = :domainId
SELECT account.balance will return the value of balance attribute of the account that was JOINed to the given domain record
FROM Domain domain says that Domain entity will be used to query the data from related table, defined on the Domain entity using #Table(name="DOMAIN"); the lowercase domain is just an alias to be used within this query (see e.g. the WHERE portion of the query)
JOIN domain.account will be used, together with annotation #OneToOne(cascade = {PERSIST, REFRESH, REMOVE}) defined on the account field of Domain entity, to get the relevant record from the table represented by Account entity
WHERE domain.id = :domainId will limit the results to return only the Domain with the given id, together with its account; the part :domainId is a "query parameter" and is most likely replaced somewhere in the code with an actual value, using code like query.setParameter("domainId", someValue);
So the whole query should return value of BigDecimal type (see field balance defined in Account entity).

Persisting Map<Entity, Double>: having BLOB instead of String key in join table

I am developing a calories calculator. I have entities Dish (edible item consisting of ingredients or other dishes) and Ingredient (atomic edible, e.g. "water" of "potatoes"). Both extends class AbstractEdible which implements interface Edible.
Dish contains field recipe of type Map<Edible, Double>, where Double represents weight of each "dish element" (ingredient or other dish).
I'm trying to persist all this stuff using JPA and Derby. I expect to have a join table with following fields:
Key of dish entity
DOUBLE value representing weight of "dish element"
Key of "dish element" entity
Here is what I have in reality:
Instead of VARCHAR key to "dish element" entity I have a BLOB and I can't figure out why. Because of that I'm having problems to update existing Dish (getting ERROR 42818: Comparisons between 'BLOB' and 'BLOB' are not supported). I will really appreaciate your help!
Dish
package com.singularityfx.kcalibri2.model.edibles;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.persistence.JoinColumn;
#Entity
public class Dish extends AbstractEdible implements EdiblesCollection {
#Transient
private static final long serialVersionUID = -5646610412222252829L;
#ElementCollection
#CollectionTable(name="dish_recipe")
#Column(name="weight")
#MapKeyJoinColumn(name="ingredient_or_dish", referencedColumnName="name")
private Map<Edible, Double> recipe = new HashMap<Edible, Double>();
#Transient
private KCalculator kCalculator = new KCalculator();
public Dish() {}
public Dish(String name, DishCategory category) {
this.name = name;
this.category = category;
}
#Override
public double getKCalNonDessert() {
kCalculator.init(recipe);
return kCalculator.getkCalNonDessertPer100g();
}
#Override
public double getKCalDessert() {
kCalculator.init(recipe);
return kCalculator.getkCalDessertPer100g();
}
#Override
public double getKCal() {
kCalculator.init(recipe);
return kCalculator.getkCalPer100g();
}
#Override
public double getKCalDessertTotal() {
kCalculator.init(recipe);
return kCalculator.getTotalKCalDessert();
}
#Override
public double getKCalNonDessertTotal() {
kCalculator.init(recipe);
return kCalculator.getTotalKCalNonDessert();
}
#Override
public double getKCalTotal() {
kCalculator.init(recipe);
return kCalculator.getTotalKCal();
}
public Double addIngredient(Edible edible, Double weight) {
return recipe.put(edible, weight);
}
public Double removeIngredient(Edible edible) {
return recipe.remove(edible);
}
#Override
public Map<Edible, Double> getContent() {
return recipe;
}
#Override
public void setContent(Map<Edible, Double> content) {
this.recipe = content;
}
#Override
public void clearContent() {
this.recipe.clear();
}
public double getWeight(Edible edible) {
return recipe.get(edible);
}
public Collection<Edible> getEdibles() {
return recipe.keySet();
}
}
Ingredient
package com.singularityfx.kcalibri2.model.edibles;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedQuery;
import javax.persistence.Transient;
#Entity
public class Ingredient extends AbstractEdible {
#Transient
private static final long serialVersionUID = -7669934586986624995L;
private double kCal;
private boolean isDessert;
public Ingredient() {}
public Ingredient(String name, IngredientCategory category,
double kCal, boolean isDessert) {
this.name = name;
this.kCal = kCal;
this.category = category;
this.isDessert = isDessert;
}
#Override
public double getKCalNonDessert() {
return isDessert ? 0 : kCal;
}
#Override
public double getKCalDessert() {
return isDessert ? kCal : 0;
}
}
AbstractEdible
package com.singularityfx.kcalibri2.model.edibles;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToOne;
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class AbstractEdible implements Edible, Serializable {
private static final long serialVersionUID = 8684184950268663225L;
#Id
protected String name;
#OneToOne()
#JoinColumn(name="NAME_OF_CATEGORY")
protected EdibleCategory category;
#Override
public String getName() {
return name;
}
#Override
public EdibleCategory getCategory() {
return category;
}
public void setName(String name) {
this.name = name;
}
public void setCategory(EdibleCategory category) {
this.category = category;
}
#Override
public String toString() {
return name + " [" + category + "]";
}
#Override
public int compareTo(Edible c) {
return name.compareTo(c.getName());
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((category == null) ? 0 : category.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractEdible other = (AbstractEdible) obj;
if (category == null) {
if (other.category != null)
return false;
} else if (!category.equals(other.category))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Edible
package com.singularityfx.kcalibri2.model.edibles;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
public interface Edible extends Comparable<Edible> {
public double getKCalNonDessert();
public double getKCalDessert();
public String getName();
public EdibleCategory getCategory();
}
Your Edible interface extends Comparable. Therefore you should override equals and hashCode, either in the Interface or in the Implementing class. The join requires comparing objects which cannot be compared unless you override equals and hashCode.
My mistake was here:
private Map<Edible, Double> recipe
Edible (interface implemented by my entities) is not an entity itself, therefore persistence provider doesn't recognize map key as entity.
When I refer to abstract class (which is annotaded with #Entity) instead of interface everything works as expected:
private Map<AbstractEdible, Double> recipe

Categories