JPA merge() updates only some fields. Other changes are not recognized - java

This is my first post. So, I hope the question is exciting.
I have a java program that uses Swing+JPA to work with a
PostgreSQL database. I use EclipseLink JPA 2.0 as my
Persistence Provider. My Entity Class was automatically
generated by Netbeans 7.2.1
The problem I facing is: during a updating, I change four
fields of an object retrieved using find(), then I use
merge() to update the object in the database. Three of
four changes are recognized and updated in the table,
but one of these is not updated.
I tried several things to get around this problem: I tried to change the options related to the synchronization strategy of my persistent unit, change the line position in the code (as other fields are updated), I also tried to prefix the Entity Class field with annotation #Basic(optional = false).. Any one of my attemps worked.
Here is the code of my Entity Class (Senha.java):
package model;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
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.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* #author Diego Dias
*/
#Entity
#Table(name = "senha")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Senha.findAll", query = "SELECT s FROM Senha s"),
#NamedQuery(name = "Senha.findById", query = "SELECT s FROM Senha s WHERE s.id = :id"),
#NamedQuery(name = "Senha.findByGuiche", query = "SELECT s FROM Senha s WHERE s.guiche = :guiche"),
#NamedQuery(name = "Senha.findByStatus", query = "SELECT s FROM Senha s WHERE s.status = :status"),
#NamedQuery(name = "Senha.findByHchamada", query = "SELECT s FROM Senha s WHERE s.hchamada = :hchamada"),
#NamedQuery(name = "Senha.findByAtendente", query = "SELECT s FROM Senha s WHERE s.atendente = :atendente"),
#NamedQuery(name = "Senha.findByHcriacao", query = "SELECT s FROM Senha s WHERE s.hcriacao = :hcriacao"),
#NamedQuery(name = "Senha.findByDtcriacao", query = "SELECT s FROM Senha s WHERE s.dtcriacao = :dtcriacao"),
#NamedQuery(name = "Senha.findByNumeracao", query = "SELECT s FROM Senha s WHERE s.numeracao = :numeracao"),
#NamedQuery(name = "Senha.findByNchamadas", query = "SELECT s FROM Senha s WHERE s.nchamadas = :nchamadas"),
#NamedQuery(name = "Senha.findByPainel", query = "SELECT s FROM Senha s WHERE s.painel = :painel"),
#NamedQuery(name = "Senha.findMaxNumeracaoByDtcriacao", query = "SELECT MAX(s.numeracao) FROM Senha s WHERE s.dtcriacao = :dtcriacao"),
#NamedQuery(name = "Senha.findByStatusAndHchamadaAndHcriacao", query = "SELECT s FROM Senha s WHERE s.status = :status AND s.hchamada <= :hchamada AND s.hcriacao >= :hcriacao")})
public class Senha implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "guiche")
private String guiche;
#Column(name = "status")
private Character status;
#Column(name = "hchamada")
#Temporal(TemporalType.TIME)
private Date hchamada;
#Column(name = "atendente")
private Integer atendente;
#Column(name = "hcriacao")
#Temporal(TemporalType.TIME)
private Date hcriacao;
#Column(name = "dtcriacao")
#Temporal(TemporalType.DATE)
private Date dtcriacao;
#Column(name = "numeracao")
private Integer numeracao;
#Column(name = "nchamadas")
private Integer nchamadas;
#Column(name = "painel")
private Boolean painel;
public Senha() {
}
public Senha(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getGuiche() {
return guiche;
}
public void setGuiche(String guiche) {
this.guiche = guiche;
}
public Character getStatus() {
return status;
}
public void setStatus(Character status) {
this.status = status;
}
public Date getHchamada() {
return hchamada;
}
public void setHchamada(Date hchamada) {
this.hchamada = hchamada;
}
public Integer getAtendente() {
return atendente;
}
public void setAtendente(Integer atendente) {
this.atendente = atendente;
}
public Date getHcriacao() {
return hcriacao;
}
public void setHcriacao(Date hcriacao) {
this.hcriacao = hcriacao;
}
public Date getDtcriacao() {
return dtcriacao;
}
public void setDtcriacao(Date dtcriacao) {
this.dtcriacao = dtcriacao;
}
public Integer getNumeracao() {
return numeracao;
}
public void setNumeracao(Integer numeracao) {
this.numeracao = numeracao;
}
public Integer getNchamadas() {
return nchamadas;
}
public void setNchamadas(Integer nchamadas) {
this.nchamadas = nchamadas;
}
public Boolean getPainel() {
return painel;
}
public void setPainel(Boolean painel) {
this.painel = painel;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.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 Senha)) {
return false;
}
Senha other = (Senha) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "model.Senha[ id=" + id + " ]";
}
}
Here is the relevant code of the method that updates my object. The code inside ... does not affect the object.
EntityManagerFactory emf = ctrlCreateEntityManager();
EntityManager em = emf.createEntityManager();
...
current = em.find(Senha.class, getCurrentId());
if (current.getStatus().equals('W')) {
em.getTransaction().begin();
current.setStatus(current.getNchamadas() >= 2 ? 'L' : current.getStatus());
current.setHchamada(Calendar.getInstance().getTime());
current.setNchamadas(current.getNchamadas() + 1);
current.setPainel(true);
em.merge(current);
em.getTransaction().commit();
frame.getLbNumeroSenha().setText(formatSenha(current.getNumeracao()));
}
...
em.close();
emf.close();
When I look in the database, the fields Status Hchamada, Nchamadas are updated, but the field Painel (of type boolean), is not updated. Follow I present a excerpt of the logging from JPA/EclipseLink:
**begin transaction**
**[EL Fine]**:
Thread(Thread[AWT-EventQueue-1,4,file:...-threadGroup])--UPDATE senha SET
hchamada = ?, nchamadas = ? WHERE (id = ?)
bind => [02:15:49, 2, 4]
**[EL Finer]**:
Thread(Thread[AWT-EventQueue-1,4,file:...-threadGroup])
**commit transaction**
Looking in the code, I put a if before change the value to see if the entity managed by JPA has already the value that I want to set. Here is the code modified:
EntityManagerFactory emf = ctrlCreateEntityManager();
EntityManager em = emf.createEntityManager();
...
current = em.find(Senha.class, getCurrentId());
if (current.getStatus().equals('W')) {
em.getTransaction().begin();
current.setStatus(current.getNchamadas() >= 2 ? 'L' : current.getStatus());
current.setHchamada(Calendar.getInstance().getTime());
current.setNchamadas(current.getNchamadas() + 1);
if (current.getPainel()) {
System.out.println("Painel is already true");
}
current.setPainel(true);
em.merge(current);
em.getTransaction().commit();
frame.getLbNumeroSenha().setText(formatSenha(current.getNumeracao()));
}
...
em.close();
emf.close();
When I run the code, I got the message that indicates that my entity is already set with the value I want to write in database. So, I think the JPA/EclipseLink does not update the value if the it does not change in relation to the entity class managed by it. However, before run the code, I manually updated the database field painel to false. THen:
I did not understand why this update is not recognized when retrieve the entity.
How to force JPA to update all fields (even that do not change)?
POSSIBLE (NOT SO GOOD) SOLUTION:
Just in the moment of post this message, I realized a possible solution (not so good). Modify the code before the update to remove the entity from the database (giving a commit), and then start other transaction, updating the object. It is, removing and persisting the object again. It works.
Before I tried use remove() and merge() inside the same transaction, but it did not work.

Depending of the implementation, EclipseLink in your case, JPA will always insert all mapped columns and will only update the columns that have changed. If alternate operations are required then the queries used for these operations can be customized using API, SQL, or Stored Procedures.
You can modify this behaviour with Hibernate dynamicInsert and dynamicUpdate, but you cannot with EclipseLink. These migration guides tells you that.
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Migration/Hibernate/V3Annotations#Dynamic_Insert.2FUpdate
http://www.eclipse.org/eclipselink/documentation/2.5/solutions/migrhib002.htm

Related

How to fix missing descriptor for class POJO after update server? [duplicate]

I'm using EclipseLink to run some Native SQL. I need to return the data into a POJO. I followed the instructions at EclipseLink Docs, but I receive the error Missing descriptor for [Class]
The query columns have been named to match the member variables of the POJO. Do I need to do some additional mapping?
POJO:
public class AnnouncementRecipientsFlattenedDTO {
private BigDecimal announcementId;
private String recipientAddress;
private String type;
public AnnouncementRecipientsFlattenedDTO() {
super();
}
public AnnouncementRecipientsFlattenedDTO(BigDecimal announcementId, String recipientAddress, String type) {
super();
this.announcementId = announcementId;
this.recipientAddress = recipientAddress;
this.type = type;
}
... Getters/Setters
Entity Manager call:
public List<AnnouncementRecipientsFlattenedDTO> getNormalizedRecipientsForAnnouncement(int announcementId) {
Query query = em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT, AnnouncementRecipientsFlattenedDTO.class);
query.setParameter(1, announcementId);
return query.getResultList();
}
I found out you can put the results of a Native Query execution into a List of Arrays that hold Objects. Then one can iterate over the list and Array elements and build the desired Entity objects.
List<Object[]> rawResultList;
Query query =
em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT);
rawResultList = query.getResultList();
for (Object[] resultElement : rawResultList) {
AnnouncementDeliveryLog adl = new AnnouncementDeliveryLog(getAnnouncementById(announcementId), (String)resultElement[1], (String)resultElement[2], "TO_SEND");
persistAnnouncementDeliveryLog(adl);
}
You can only use native SQL queries with a class if the class is mapped. You need to define the AnnouncementRecipientsFlattenedDTO class as an #Entity.
Otherwise just create the native query with only the SQL and get an array of the data back and construct your DTO yourself using the data.
Old question but may be following solution will help someone else.
Suppose you want to return a list of columns, data type and data length for a given table in Oracle. I have written below a native sample query for this:
private static final String TABLE_COLUMNS = "select utc.COLUMN_NAME, utc.DATA_TYPE, utc.DATA_LENGTH "
+ "from user_tab_columns utc "
+ "where utc.table_name = ? "
+ "order by utc.column_name asc";
Now the requirement is to construct a list of POJO from the result of above query.
Define TableColumn entity class as below:
#Entity
public class TableColumn implements Serializable {
#Id
#Column(name = "COLUMN_NAME")
private String columnName;
#Column(name = "DATA_TYPE")
private String dataType;
#Column(name = "DATA_LENGTH")
private int dataLength;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public TableColumn(String columnName, String dataType, int dataLength) {
this.columnName = columnName;
this.dataType = dataType;
this.dataLength = dataLength;
}
public TableColumn(String columnName) {
this.columnName = columnName;
}
public TableColumn() {
}
#Override
public int hashCode() {
int hash = 0;
hash += (columnName != null ? columnName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof TableColumn)) {
return false;
}
TableColumn other = (TableColumn) object;
if ((this.columnName == null && other.columnName != null) || (this.columnName != null && !this.columnName.equals(other.columnName))) {
return false;
}
return true;
}
#Override
public String toString() {
return getColumnName();
}
}
Now we are ready to construct a list of POJO. Use the sample code below to construct get your result as List of POJOs.
public List<TableColumn> findTableColumns(String table) {
List<TableColumn> listTables = new ArrayList<>();
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery(TABLE_COLUMNS, TableColumn.class).setParameter(1, table);
listTables = q.getResultList();
em.close();
return listTables;
}
Also, don't forget to add in your POJO class in persistence.xml! It can be easy to overlook if you are used to your IDE managing that file for you.
Had the same kind of problem where I wanted to return a List of POJOs, and really just POJOs (call it DTO if you want) and not #Entity annotated Objects.
class PojoExample {
String name;
#Enumerated(EnumType.STRING)
SomeEnum type;
public PojoExample(String name, SomeEnum type) {
this.name = name;
this.type = type;
}
}
With the following Query:
String query = "SELECT b.name, a.newtype as type FROM tablea a, tableb b where a.tableb_id = b_id";
Query query = getEntityManager().createNativeQuery(query, "PojoExample");
#SuppressWarnings("unchecked")
List<PojoExample> data = query.getResultList();
Creates the PojoExample from the database without the need for an Entity annotation on PojoExample. You can find the method call in the Oracle Docs here.
edit:
As it turns out you have to use #SqlResultSetMapping for this to work, otherwise your query.getResultList() returns a List of Object.
#SqlResultSetMapping(name = "PojoExample",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "type", type = String.class)
},
targetClass = PojoExample.class)
)
Just put this anywhere under your #Entity annotation (so in this example either in tablea or tableb because PojoExample has no #Entity annotation)

New to Hibernate/JPA: java.lang.NullPointerException filtering on table primary key

I'm new to Hibernate/JPA. I wrote a simple demo project to start understanding the technology. I have this problem: everything works fine but filtering the persistence object on table primary key field. Every other field can be used to filter the objects without any problem even when I use multiple filters.
This is the simple demo code I wrote:
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<BaRoles> criteriaQuery = criteriaBuilder.createQuery(BaRoles.class);
Root<BaRoles> root = criteriaQuery.from(BaRoles.class);
criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("rocode"), 78));
//criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("rodescri"), "xxxxxxxx")); <-- Works fine
Query<BaRoles> query = session.createQuery(criteriaQuery);
List<BaRoles> roles = query.getResultList(); // <-- Returns nothing filtering rocode, correctly returns obejcts when filtering rodescri
for (int roleCounter = 0; roleCounter < roles.size(); roleCounter++) {
BaRoles role = (BaRoles) roles.get(roleCounter);
}
This is the complete stack trace:
java.lang.NullPointerException
at org.hibernate.query.sqm.sql.internal.BasicValuedPathInterpretation.from(BasicValuedPathInterpretation.java:50)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitBasicValuedPath(BaseSqmToSqlAstConverter.java:747)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitBasicValuedPath(BaseSqmToSqlAstConverter.java:158)
at org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath.accept(SqmBasicValuedSimplePath.java:87)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:1545)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:158)
at org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate.accept(SqmComparisonPredicate.java:67)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:364)
at org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator.visitSelectStatement(StandardSqmSelectTranslator.java:169)
at org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator.translate(StandardSqmSelectTranslator.java:111)
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:224)
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.resolveCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:191)
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:153)
at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:413)
at org.hibernate.query.spi.AbstractQuery.list(AbstractQuery.java:1356)
at org.hibernate.query.Query.getResultList(Query.java:136)
at com.xxxx.xxxxx.mavenproject1.BaRoleManager.readWithFilter(BaRoleManager.java:153)
at com.xxxx.xxxxx.mavenproject1.BaRoleManager.main(BaRoleManager.java:201)
And this is the BaRoles class (Generate with JPA):
import java.io.Serializable;
import javax.persistence.Basic;
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.Table;
import javax.persistence.TableGenerator;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "ba_roles")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "BaRoles.findAll", query = "SELECT b FROM BaRoles b"),
#NamedQuery(name = "BaRoles.findByRocode", query = "SELECT b FROM BaRoles b WHERE b.rocode = :rocode"),
#NamedQuery(name = "BaRoles.findByRodescri", query = "SELECT b FROM BaRoles b WHERE b.rodescri = :rodescri"),
#NamedQuery(name = "BaRoles.findByCpccchk", query = "SELECT b FROM BaRoles b WHERE b.cpccchk = :cpccchk"),
#NamedQuery(name = "BaRoles.findByRodescriEng", query = "SELECT b FROM BaRoles b WHERE b.rodescriEng = :rodescriEng")
})
public class BaRoles implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
//#TableGenerator(name = "roleGenerator", table = "cpwarn", allocationSize = 1, pkColumnName = "tablecode", valueColumnName = "autonum", pkColumnValue = "prog\\ba_roles", schema="dbo", initialValue = 0)
#TableGenerator(name = "roleGenerator", table = "cpwarn", allocationSize = 1, pkColumnName = "tablecode", valueColumnName = "autonum", pkColumnValue = "prog\\ba_roles")
#GeneratedValue(strategy = GenerationType.TABLE, generator = "roleGenerator")
#Column(name = "ROCODE", updatable = false, nullable = false)
private Integer rocode;
#Column(name = "RODESCRI")
private String rodescri;
#Column(name = "cpccchk")
private String cpccchk;
#Column(name = "RODESCRI_ENG")
private String rodescriEng;
public BaRoles() {
}
public BaRoles(Integer rocode) {
this.rocode = rocode;
}
public Integer getRocode() {
return rocode;
}
public void setRocode(Integer rocode) {
this.rocode = rocode;
}
public String getRodescri() {
return rodescri;
}
public void setRodescri(String rodescri) {
this.rodescri = rodescri;
}
public String getCpccchk() {
return cpccchk;
}
public void setCpccchk(String cpccchk) {
this.cpccchk = cpccchk;
}
public String getRodescriEng() {
return rodescriEng;
}
public void setRodescriEng(String rodescriEng) {
this.rodescriEng = rodescriEng;
}
#Override
public int hashCode() {
int hash = 0;
hash += (rocode != null ? rocode.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 BaRoles)) {
return false;
}
BaRoles other = (BaRoles) object;
if ((this.rocode == null && other.rocode != null) || (this.rocode != null && !this.rocode.equals(other.rocode))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.xxxx.xxxx.mavenproject1.BaRoles[ rocode=" + rocode + " ]";
}
}
Solved! Reverted Hibernate version from 6.0.0 alpha 4 to 5.4.12 final (Maven repository). Everything works as expected. Thank you everyone for your time and effort.

Binding from database in swing combobox in java

I want to display distinct year in combo box in ascending order. But the output in combox appears to be repetitive, repetitive in the sense that it shows all the elements of the column of database but i want to show only the distinct data of the column . What am i doing wrong?? I have simply done simple database query before in php. I know it is simply but i am not being to address the query in right order, i guess.
My code is as follows.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package my_ui;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import javax.persistence.Basic;
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.Table;
import javax.persistence.Transient;
/**
*
* #author enjal
*/
#Entity
#Table(name = "production", catalog = "data2", schema = "")
#NamedQueries({
#NamedQuery(name = "Production.findAll", query = "SELECT p FROM Production p"),
#NamedQuery(name = "Production.findByProductionId", query = "SELECT p FROM Production p WHERE p.productionId = :productionId"),
#NamedQuery(name = "Production.findByCropId", query = "SELECT p FROM Production p WHERE p.cropId = :cropId"),
#NamedQuery(name = "Production.findByLocationId", query = "SELECT p FROM Production p WHERE p.locationId = :locationId"),
#NamedQuery(name = "Production.findByArea", query = "SELECT p FROM Production p WHERE p.area = :area"),
#NamedQuery(name = "Production.findByProductionAmount", query = "SELECT p FROM Production p WHERE p.productionAmount = :productionAmount"),
#NamedQuery(name = "Production.findByYieldAmount", query = "SELECT p FROM Production p WHERE p.yieldAmount = :yieldAmount"),
#NamedQuery(name = "Production.findByYearOfProduction", query = "SELECT DISTINCT p FROM Production p WHERE p.yearOfProduction = :yearOfProduction ORDER BY p.yearOfProduction ASC" )})
//SELECT DISTINCT year_of_production FROM `production` ORDER BY year_of_production ASC
public class Production implements Serializable {
#Transient
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "production_id")
private Integer productionId;
#Basic(optional = false)
#Column(name = "crop_id")
private int cropId;
#Basic(optional = false)
#Column(name = "location_id")
private String locationId;
#Basic(optional = false)
#Column(name = "area")
private double area;
#Basic(optional = false)
#Column(name = "production_amount")
private String productionAmount;
#Basic(optional = false)
#Column(name = "yield_amount")
private double yieldAmount;
#Basic(optional = false)
#Column(name = "year_of_production")
private String yearOfProduction;
public Production() {
}
public Production(Integer productionId) {
this.productionId = productionId;
}
public Production(Integer productionId, int cropId, String locationId, double area, String productionAmount, double yieldAmount, String yearOfProduction) {
this.productionId = productionId;
this.cropId = cropId;
this.locationId = locationId;
this.area = area;
this.productionAmount = productionAmount;
this.yieldAmount = yieldAmount;
this.yearOfProduction = yearOfProduction;
}
public Integer getProductionId() {
return productionId;
}
public void setProductionId(Integer productionId) {
Integer oldProductionId = this.productionId;
this.productionId = productionId;
changeSupport.firePropertyChange("productionId", oldProductionId, productionId);
}
public int getCropId() {
return cropId;
}
public void setCropId(int cropId) {
int oldCropId = this.cropId;
this.cropId = cropId;
changeSupport.firePropertyChange("cropId", oldCropId, cropId);
}
public String getLocationId() {
return locationId;
}
public void setLocationId(String locationId) {
String oldLocationId = this.locationId;
this.locationId = locationId;
changeSupport.firePropertyChange("locationId", oldLocationId, locationId);
}
public double getArea() {
return area;
}
public void setArea(double area) {
double oldArea = this.area;
this.area = area;
changeSupport.firePropertyChange("area", oldArea, area);
}
public String getProductionAmount() {
return productionAmount;
}
public void setProductionAmount(String productionAmount) {
String oldProductionAmount = this.productionAmount;
this.productionAmount = productionAmount;
changeSupport.firePropertyChange("productionAmount", oldProductionAmount, productionAmount);
}
public double getYieldAmount() {
return yieldAmount;
}
public void setYieldAmount(double yieldAmount) {
double oldYieldAmount = this.yieldAmount;
this.yieldAmount = yieldAmount;
changeSupport.firePropertyChange("yieldAmount", oldYieldAmount, yieldAmount);
}
public String getYearOfProduction() {
return yearOfProduction;
}
public void setYearOfProduction(String yearOfProduction) {
String oldYearOfProduction = this.yearOfProduction;
this.yearOfProduction = yearOfProduction;
changeSupport.firePropertyChange("yearOfProduction", oldYearOfProduction, yearOfProduction);
}
#Override
public int hashCode() {
int hash = 0;
hash += (productionId != null ? productionId.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 Production)) {
return false;
}
Production other = (Production) object;
if ((this.productionId == null && other.productionId != null) || (this.productionId != null && !this.productionId.equals(other.productionId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "my_ui.Production[ productionId=" + yearOfProduction + " ]";
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
}
I want to display distinct year in combo box in ascending order. But the output in combobox appears to be repetitive, repetitive in the sense that it shows all the elements of the column of database but i want to show only the distinct data of the column
And you say this is the query you are using to accomplish that
#NamedQuery(name = "Production.findByYearOfProduction",
query = "SELECT DISTINCT p FROM Production p
WHERE p.yearOfProduction = :yearOfProduction
ORDER Bp.yearOfProduction ASC" )})
The problem with this query is that it's used to find a list Production entities by the yearOfProduction. So say you use this query with the year 1990. That means that any Production entity that was produced in 1990 with be part of the result set. So the only year of production you will see is 1990.
What you want is just the distinct values in a single column, where the actual Production entity is not necessarily concerned. So your query should look more like
SELECT DISTINCT p.yearOfProduction
FROM Production p
ORDER BY p.yearOfProduction ASC
The return value should be a list of just the distinct years, and having no connection to the Production entity. You may want to do some refactoring to your code, where calling this query will return a List<String>. And that's the list you want to use for your combo box, not a List<Production>. Where should you do this refactoring? I can't tell, as you haven't provided the code where you call this query.
Note: So what you should do is create a separate #NamedQuery, and the one you currently have, I think you want to keep for maybe a different combo box maybe, where after a year is selected from the first combo box, the second one is populate with all Production entities for that year. Also note your toString just lists the year of production. So if you want some other representation of the Production entity in a the second combo box, you should change that also.
Also keep in mind when you auto-create an entity in Netbeans, It will create a findAll query and a query findByXxx query for each property of the entity. That's it. All the queries are meant to return a list of Production entities using one that property as the parameter for finding matching results. Any other return types you want (in this case a list of strings) or different parameter queries, you need to create a customized query

JPA/Hibernate doing update in table without any command

Well, I work with JSF + Hibernate/JPA, I have the following code:
public void loadAddressByZipCode(ActionEvent event){
Address address = boDefault.findByNamedQuery(Address.FindByZipCode, 'zipCode', bean.getAddress().getZipCode());
if (address == null){
//This zip code not exists in db
}else{
bean.setAddress(address);
}
}
This method above is called in every "onBlur" in inputText component in XHTML page, this inputText have the property value like this: "#{addressMB.bean.address.zipCode}"
So, when user (in XHTML page) type a new Zip Code this value is setted in "bean.getAddress().setZipCode()" and i search for this value in Database. But debugging my application i discovered that when i do "bean.getAddress().getZipCode()" the Hibernate launch a "update address set ..." in my DataBase. How can i prevent this and why this happens ?
EDIT 1:
This is my real method implemented
public void carregarLogradouroByCep(AjaxBehaviorEvent event) {
List listReturn = getBoPadrao().findByNamedQuery(
Logradouro.FIND_BY_CEP,
new NamedParams("cep", bean.getLogradouro().getCep()));
if (listReturn.size() > 0) {
Logradouro logradouro = (Logradouro) listReturn.get(0);
bean.setLogradouro(logradouro);
}else{
bean.setLogradouro(new Logradouro());
}
}
This is my component with onblur event:
<p:inputText value="#{enderecoMB.bean.logradouro.cep}" id="cep"
required="true" requiredMessage="O CEP é obrigatório">
<p:ajax event="blur"
listener="#{enderecoMB.carregarLogradouroByCep}"
update=":formManterEndereco:logradouro, :formManterEndereco:cidade, :formManterEndereco:estado,
:formManterEndereco:bairro" />
</p:inputText>
I have a "BasicDAOImpl" that make all operations in database, so tried do it in my entityManager:
#PostConstruct
private void init(){
entityManager.setFlushMode(FlushModeType.COMMIT);
}
But "update automatically" continue.
EDIT 2: My FIND_BY_CEP query
"SELECT c FROM Endereco c JOIN FETCH c.tipoEndereco JOIN FETCH c.logradouro"
EDit 3: My Entities
Endereco.java
#Entity
#NamedQueries(value = { #NamedQuery(name = "Endereco.findAllCompleto", query = "SELECT c FROM Endereco c "
+ "JOIN FETCH c.tipoEndereco " + "JOIN FETCH c.logradouro") })
#Table(name = "endereco")
public class Endereco extends AbstractBean {
/**
*
*/
private static final long serialVersionUID = 5239354646908722819L;
public Endereco(){
logradouro = new Logradouro();
}
#Transient
public static final String FIND_ALL_COMPLETO = "Endereco.findAllCompleto";
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_tipo_endereco")
private TipoEndereco tipoEndereco;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_logradouro")
private Logradouro logradouro;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_pessoa")
private Pessoa pessoa;
#Column
private String numero;
#Column
private String complemento;
public Logradouro getLogradouro() {
return logradouro;
}
public void setLogradouro(Logradouro logradouro) {
this.logradouro = logradouro;
}
public TipoEndereco getTipoEndereco() {
return tipoEndereco;
}
public void setTipoEndereco(TipoEndereco tipoEndereco) {
this.tipoEndereco = tipoEndereco;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public String getComplemento() {
return complemento;
}
public void setComplemento(String complemento) {
this.complemento = complemento;
}
public Pessoa getPessoa() {
return pessoa;
}
public void setPessoa(Pessoa pessoa) {
this.pessoa = pessoa;
}
}
Logradouro.java
#Entity
#NamedQueries(value = { #NamedQuery(name = "Logradouro.findByCep", query = "SELECT c FROM Logradouro c "
+ "JOIN FETCH c.bairro "
+ "JOIN FETCH c.cidade "
+ "WHERE c.cep = :cep "
+ "ORDER BY c.logradouro") })
#Table(name = "logradouro")
public class Logradouro extends AbstractBean {
public Logradouro(){
this.cidade = new Cidade();
this.bairro = new Bairro();
}
/**
*
*/
private static final long serialVersionUID = 1L;
#Transient
public static final String FIND_BY_CEP = "Logradouro.findByCep";
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_cidade")
private Cidade cidade;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_bairro")
private Bairro bairro;
#Column
private String cep;
#Column
private String logradouro;
public Cidade getCidade() {
return cidade;
}
public void setCidade(Cidade cidade) {
this.cidade = cidade;
}
public Bairro getBairro() {
return bairro;
}
public void setBairro(Bairro bairro) {
this.bairro = bairro;
}
public String getCep() {
return cep;
}
public void setCep(String cep) {
this.cep = cep;
}
public String getLogradouro() {
return logradouro;
}
public void setLogradouro(String logradouro) {
this.logradouro = logradouro;
}
}
Hibernate uses an entitymanager to do automatic handling of your entities by default. Your bean is one such entity so whenever the entitymanager feels it's time to flush to the database it does so. If you want to flush manually, you'll want to have a look at such a construction:
entityManager.setFlushMode(javax.persistence.FlushModeType.MANUAL);
I think that you misuse Hibernate. The main idea of ORM that is allows you to write code, ignoring that fact, that something goes into database. Your domain model must be completetly testable without any database. ORM is not abstraction to manage with relation database, it is abstraction for your object model. If you need to explicitly do inserts and updates, you better use some active record implementation or look at lightweight mapper like MyBatis.
And why you want to prevent database update? Property is changed, right? So let Hibernate decide, whether and when its new value must be written to database.

Java EE 6, EJB 3.1, JPA 2.0 - error in inserting record in database

I am trying to insert a record in the database (using Java EE 6, EJB 3.1, JPA 2.0). I am getting an error that accountTypeId field is null, but i have set it up as autogenerate. Can anyone please suggest what am I doing wrong?
Following is the create table query:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
account_type_desc varchar(20)
);
Following is the entity class:
EDIT: Updated the entity class as generated by NetBeans which didn't work. I also added #GeneratedValue annotation but still it didn't work.
#Entity
#Table(name = "ACCOUNT_TYPE")
#NamedQueries({
#NamedQuery(name = "AccountType.findAll", query = "SELECT a FROM AccountType a"),
#NamedQuery(name = "AccountType.findByAccountTypeId", query = "SELECT a FROM AccountType a WHERE a.accountTypeId = :accountTypeId"),
#NamedQuery(name = "AccountType.findByAccountTypeDesc", query = "SELECT a FROM AccountType a WHERE a.accountTypeDesc = :accountTypeDesc")})
public class AccountType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) // ADDED THIS LINE
#Basic(optional = false)
#NotNull
#Column(name = "ACCOUNT_TYPE_ID")
private Integer accountTypeId;
#Size(max = 50)
#Column(name = "ACCOUNT_TYPE_DESC")
private String accountTypeDesc;
public AccountType() {
}
public AccountType(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public Integer getAccountTypeId() {
return accountTypeId;
}
public void setAccountTypeId(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public String getAccountTypeDesc() {
return accountTypeDesc;
}
public void setAccountTypeDesc(String accountTypeDesc) {
this.accountTypeDesc = accountTypeDesc;
}
#Override
public int hashCode() {
int hash = 0;
hash += (accountTypeId != null ? accountTypeId.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 AccountType)) {
return false;
}
AccountType other = (AccountType) object;
if ((this.accountTypeId == null && other.accountTypeId != null) || (this.accountTypeId != null && !this.accountTypeId.equals(other.accountTypeId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Entities.AccountType[ accountTypeId=" + accountTypeId + " ]";
}
}
Following is the session bean interface:
#Remote
public interface AccountTypeSessionBeanRemote {
public void createAccountType();
public void createAccountType(String accDesc);
}
Following is the session bean implementation class:
#Stateless
public class AccountTypeSessionBean implements AccountTypeSessionBeanRemote {
#PersistenceContext(unitName="ExamplePU")
private EntityManager em;
#Override
public void createAccountType(String accDesc) {
AccountType emp = new AccountType(accDsc);
try {
this.em.persist(emp);
System.out.println("after persist");
} catch(Exception ex) {
System.out.println("ex: " + ex.getMessage());
}
}
}
Following is the Main class:
public class Main {
#EJB
private static AccountTypeSessionBeanRemote accountTypeSessionBean;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
accountTypeSessionBean.createAccountType("test");
}
}
Following is the error:
INFO: ex: Object: Entities.AccountType[ accountTypeId=null ] is not a known entity type.
You are not getting an error because of "accountTypeId field is null". As the error message says, the error occurs because "Entities.AccountType[ accountTypeId=null ] is not a known entity type".
The most likely reason is that AccountType is not annotated with #Entity. This problem is likely solved by adding it. Additionally it makes sense to use
#GeneratedValue(strategy = GenerationType.IDENTITY)
instead of AUTO. Auto means that the provider chooses a strategy based on the capabilities of the target database. According to the table definition it seems clear that the preferred strategy is IDENTITY.
I changed my create table query as following:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY,
account_type_desc varchar(20)
);
Then had to add the following line to the entity class (Netbeans doesn't add that):
#GeneratedValue(strategy = GenerationType.AUTO)

Categories