I have a Hibernate mapping setup. The table is species, my Java class is Species. hibernate.cfg.xml points to mappings in species.hbn.xml
In my code I'm using a simple HQL query and then throwing the resultant Species instances into a "SpeciesLister" class (which I'm passing over to the presentation layer).
SpeciesLister speciesList = new SpeciesLister();
Query q = session.createQuery("SELECT s FROM Species s");
for (Species s : (List<Species>) q.list()){
speciesList.addSpecies(s);
}
The Species class looks like this:
package springwildlife;
public class Species implements Serializable
{
long id;
String commonName;
String latinName;
String order;
String family;
ArrayList<Sighting> sightings;
public Species()
{
}
public Species(String commonName, String latinName)
{
sightings = new ArrayList<Sighting>();
this.commonName = commonName;
this.latinName = latinName;
}
public long getId()
{
return id;
}
public String getCommonName()
{
return commonName;
}
public String getLatinName()
{
return latinName;
}
public String getOrder()
{
return order;
}
public String getFamily()
{
return family;
}
public ArrayList<Sighting> getSightings()
{
return sightings;
}
public void addSighting(Sighting s)
{
sightings.add(s);
}
public void setId(long id)
{
this.id = id;
}
public void setCommonName(String cn)
{
commonName = cn;
}
public void setLatinName(String ln)
{
commonName = ln;
}
public void setFamily(String f)
{
family = f;
}
public void setOrder(String o)
{
order = o;
}
}
My database schema looks like this:
CREATE TABLE species
(
id serial NOT NULL,
common_name text,
latin_name text,
order_name text,
family_name text,
CONSTRAINT id PRIMARY KEY (id)
)
species.hbn.xml looks like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="springwildlife.Species" table="species">
<id name="id" type="java.lang.Long" column="id" >
<generator class="native">
<param name="sequence">species_id_seq</param>
</generator>
</id>
<property name="commonName" type="java.lang.String">
<column name="common_name" />
</property>
<property name="latinName" type="java.lang.String">
<column name="latin_name"/>
</property>
<property name="order" type="java.lang.String">
<column name="order_name"/>
</property>
<property name="family" type="java.lang.String">
<column name="family_name"/>
</property>
</class>
</hibernate-mapping>
My SpeciesLister instance gets a full slate of all the expected number of Species instances. However, when I examine the resultant Species instances, all their fields are null except for the id (long), all the others like familyName, latinName, commonName all are null in the mapped object.
This is unexpected and I can't figure out why it is happening. Am I doing something wrong?
I'm suspicious about two things, but I'm not sure of what to make of them:
I think the fact that the id is being property set, but not the other string fields might be a clue.
I suspect something might be wrong with the way I'm casting the objects into a list of Species instances.
The code looks ok. Without getting into debugger it's hard to tell for sure, however my guess is that you have compile time class instrumentation somewhere in the build. If that's a case, I've seen cases when assignment to actual field in the class is deferred until you call getter method.
So I suggest, that you put some print statements that rely on getters to get data instead of direct access to properties and see what gets printed.
Finally, please put # sign in front of names in comments (#Mark). This way, your correspondents will get notified and you may get response sooner.
Related
I'm using spring hibernate for a web application. It has a OpenHours modal which return the open time. It has a field sundayOpen type="time". Whenever the time in database column 'sundayOpen' is equal to '00:00:00' the sundayOpen is returning null. But all the other times it returns the correct time.
I have experienced this issue with hibernate 5.4.2 and 3.6.0.
Is there any way to get '00:00:00' instead of null?
OpenHours.hbm.xml
<class name="com.example.OpenHours" table="openHours" schema="abxd_db">
<id name="id" type="long">
<column name="id"/>
<generator class="assigned"/>
</id>
<property name="sundayOpen" type="time">
<column length="16" name="sundayOpen"/>
</property>
</class>
OpenHours.java
public class OpenHours implements Serializable {
private long id;
private Time sundayOpen;
#Id
#Column(name = "id", nullable = false)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Time getSundayOpen() {
return sundayOpen;
}
public void setSundayOpen(Time sundayOpen) {
this.sundayOpen = sundayOpen;
}
}
You can create a custom UserType,
public class CustomTimeUserType implements UserType {
public void nullSafeSet(PreparedStatement ps, Object value, int index) throws HibernateException, SQLException {
if (value == null)
ps.setString(index, "00:00:00");
else
ps.setDate(index, (Time) value);
}
// implement other methods
}
And use it in property,
<property name="sundayOpen" type="time">
<column length="16" name="com.pkg.CustomTimeUserType"/>
</property>
I am not sure but you can also try with zeroDateTimeBehavior,
hibernate.connection.zeroDateTimeBehavior=round
I have app which is storing parsed urls inside MySQL db and populating parsed sentences, then parsed Words etc. using cascade (so I just save url, db insert rest automatically).
In the ProcessedUrl POJO I have fields:
Long id
String url
Date date
Set<Sentence> sentences
For now PK is id which is generated in native way. I want to achieve something like that - when user enter some url which is already parsed and stored into db, it won't be parsed again/duplicated.
What is suitable way of achieving this using hbm.xml mapping?
EDIT:
ProcessedUrl POJO:
public class ProcessedUrl {
private long id;
private String url;
private Date date;
private Set<Sentence> sentences;
public ProcessedUrl() {
}
public ProcessedUrl(String url, Date date) {
this.setUrl(url);
this.setDate(date);
}
public ProcessedUrl(String url, Date date, Set<Sentence> sentences) {
this.setUrl(url);
this.setDate(date);
this.setSentences(sentences);
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Set<Sentence> getSentences() {
return this.sentences;
}
public void setSentences(Set<Sentence> sentences) {
this.sentences = sentences;
}
#Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof ProcessedUrl)) return false;
ProcessedUrl that = (ProcessedUrl) obj;
EqualsBuilder eb = new EqualsBuilder();
eb.append(this.getUrl(), that.getUrl());
return eb.isEquals();
}
#Override
public int hashCode() {
HashCodeBuilder hcb = new HashCodeBuilder();
hcb.append(url);
return hcb.toHashCode();
}
}
ProcessedUrl.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="model">
<class name="ProcessedUrl">
<id name="id">
<column name="url_id" />
<generator class="native"/>
</id>
<property name="url" type="text"/>
<property name="date" type="java.util.Date" />
<set name="sentences" cascade="all" >
<key column="PROCESSED_URL_ID" />
<one-to-many class="model.Sentence" />
</set>
</class>
</hibernate-mapping>
When user enters some url, which is already parsed and stored into db,
it won't be parsed again/duplicated. What is suitable way of achieving
this using hbm.xml mapping ?
You can't do this with simple hbm mappings because the previously parsed URL is already stored in the database and you need to validate that the latest received URL value already exists in the database.
So, to achieve this, you need to follow the below steps:
(1) Get the URL from App (DO NOT PARSE HERE)
(2) Check the URL already exists in database
(3) If URL does NOT exist, PARSE THE URL NOW and save to database
(4) If URL already exists, log an warning/error or ignore the request
I have this Oracle table:
SQL> Name Null? Type
----------------------------------------- -------- ----------------------------
JOB_ID NOT NULL VARCHAR2(13)
TYPE NOT NULL NUMBER
COMPONENT_DESCRIPTION NOT NULL VARCHAR2(255)
COMPONENT_ID VARCHAR2(13)
STATUS NOT NULL NUMBER(1)
REASON VARCHAR2(255)
NOTES VARCHAR2(255)
SQL>
There is no defined primary key but JOB_ID, TYPE and COMPONENT_DESCRIPTION combined are unique. I cannot make any changes to the database structure and the code I'm working will only ever read from the DB, it will never write to it.
I have made this Hibernate map file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"classpath://org/hibernate/hibernate-mapping-3.0.dtd">
<hibernate-mapping schema="ARCHIVE">
<class name="myclass.ArchiveJobHeaderComponents" table="JOB_HEADER_COMPONENTS">
<composite-id>
<key-property name="jobId" column="JOB_ID" type="java.lang.String" />
<key-property name="type" column="TYPE" type="java.lang.Number" />
<key-property name="componentDescription" column="COMPONENT_DESCRIPTION" type="java.lang.String" />
</composite-id>
<property name="componentId" column="COMPONENT_ID" type="java.lang.String" not-null="false" />
<property name="status" column="STATUS" type="java.lang.Number" />
<property name="reason" column="REASON" type="java.lang.String" not-null="false" />
<property name="notes" column="NOTES" type="java.lang.String" not-null="false" />
</class>
<query name="JobHeaderComponents.lookupJobHeaderComponents">
<![CDATA[from myclass.ArchiveJobHeaderComponents where
jobId = :jobId and
type = :type and
componentDescription = :componentDescription ]]>
</query>
<query name="JobHeaderComponents.listJobHeaderComponentsByComponentId">
<![CDATA[from myclass.ArchiveJobHeaderComponents where componentId = :id]]>
</query>
</hibernate-mapping>
This is the corresponding Java class file:
package myclass;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import java.io.Serializable;
import java.lang.Number;
import java.util.HashSet;
public class ArchiveJobHeaderComponents implements Serializable {
private String jobId;
private Number type;
private String componentDescription;
private String componentId;
private Number status;
private String reason;
private String notes;
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public Number getType() {
return type;
}
public void setType(Number type) {
this.type = type;
}
public String getComponentDescription() {
return componentDescription;
}
public void setComponentDescription(String componentDescription) {
this.componentDescription = componentDescription;
}
public String getComponentId() {
return componentId;
}
public void setComponentId(String componentId) {
this.componentId = componentId;
}
public Number getStatus() {
return status;
}
public void setStatus(Number status) {
this.status = status;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public int hashCode() {
return new HashCodeBuilder().
append(getJobId()).
append(getType()).
append(getComponentDescription()).
append(getComponentId()).
append(getStatus()).
append(getReason()).
append(getNotes()).toHashCode();
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ArchiveJobHeaderComponents)) {
return false;
}
ArchiveJobHeaderComponents that = (ArchiveJobHeaderComponents) o;
return new EqualsBuilder().append(this.getJobId(), that.getJobId()).
append(this.getType(), that.getType()).
append(this.getComponentDescription(), that.getComponentDescription()).
append(this.getComponentId(), that.getComponentId()).
append(this.getStatus(), that.getStatus()).
append(this.getReason(), that.getReason()).
append(this.getNotes(), that.getNotes()).isEquals();
}
public String toString() {
return new ToStringBuilder(this).
append("jobId", getJobId()).
append("type", getType()).
append("componentDescription", getComponentDescription()).
append("componentId", getComponentId()).
append("status", getStatus()).
append("reason", getReason()).
append("notes", getNotes()).toString();
}
}
Whenever I get data back from a query, I get 'Could not deserialize' followed by an 'EOFException' error.
I've checked:
- There are no variables in the Java class of type serialize
- The Java class is implementing Serializable
I don't want to split the three columns (JOB_ID, TYPE and COMPONENT_DESCRIPTION) into a separate 'Id' class as I'm having conceptual problems with how the data is accessed. (I realize this is not recommended but is supported).
Can anyone point out what I've done wrong with how I've implemented this?
Thanks
EDIT:
I've changed the hbm.xml to not have a composite key, just an id on JOB_ID with no improvement.
I've added not-null="false" to the columns that can be empty, also no improvement.
Actually, having a look over the code and the Hibernate mapping file, I believe that the problem is that you are trying to map columns TYPE and STATUS to a Number. Number is an abstract class, so cannot be instantiated directly.
As both TYPE and STATUS are NOT NULL, I'd use primitive Java types to store their values, eg:
public class ArchiveJobHeaderComponents implements Serializable {
private String jobId;
private int type; // int should give you a large enough range - but change to long if required
private String componentDescription;
private String componentId;
private boolean status; // status appears to be a boolean (NUMBER(1))
private String reason;
private String notes;
// remainder omitted
}
Also, please remember to update the Hibernate mapping file to reflect the above!!
In case anyone else is working on a legacy Hibernate (3.0) app, something else that causes this error is running the app with Java 8 and OJDBC 1.4. Upgrading to OJDBC 6 resolved it.
I have a class like this,
class SampleClass implements Serializable {
String name;
Serializable fieldName;
}
And another class like,
class AnotherClass implements Serializable {
SampleClass sampleClass;
}
where both class has their getter and setter methods.
In the main class, I'm getting the sampleClass variable from getter function, and trying to use the sampleClass objects. But when I'm using that, I encounter the error like, could not deserialize.
How to access the members of SampleClass, or shall we have field members of type Serializable ?
Thanks.
Edited:
I am using hibernate, which uses many to one relation between aemploye and aaddress tables.
I created the Hibernate configuration file, and Reverse engineering file in net beans, for the above two tables.
Then I generated the POJO class.
The class and xml are:
Aaddress.hbm.xml
<hibernate-mapping>
<class name="hibernatetutor.tablebeans.Aaddress" table="aaddress" schema="public">
<id name="sno" type="int">
<column name="sno" />
<generator class="assigned" />
</id>
<property name="street" type="serializable">
<column name="street" />
</property>
<set name="aemployes" inverse="true">
<key>
<column name="address" />
</key>
<one-to-many class="hibernatetutor.tablebeans.Aemploye" />
</set>
</class>
Aemploye.hbm.xml
<hibernate-mapping>
<class name="hibernatetutor.tablebeans.Aemploye" table="aemploye" schema="public">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<many-to-one name="aaddress" class="hibernatetutor.tablebeans.Aaddress" fetch="select">
<column name="address" />
</many-to-one>
<property name="name" type="string">
<column name="name" />
</property>
</class>
Aaddress.java
public class Aaddress implements java.io.Serializable {
private int sno;
private Serializable street;
private Set aemployes = new HashSet(0);
public int getSno() {
return this.sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public Serializable getStreet() {
return this.street;
}
public void setStreet(Serializable street) {
this.street = street;
}
public Set getAemployes() {
return this.aemployes;
}
public void setAemployes(Set aemployes) {
this.aemployes = aemployes;
}
}
Aemploye.java
public class Aemploye implements java.io.Serializable {
private int id;
private Aaddress aaddress;
private String name;
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public Aaddress getAaddress() {
return this.aaddress;
}
public void setAaddress(Aaddress aaddress) {
this.aaddress = aaddress;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Main.java
private void getData() {
Session session = HibernateUtils.getInstance().openSession();
Query query = session.createQuery("from Aemploye where id=:id");
query.setParameter("id", 1);
Aemploye a = (Aemploye) query.uniqueResult();
Aaddress a1 = a.getAaddress();
System.out.println(a1.getStreet());
}
The error is:
org.hibernate.type.SerializationException: could not deserialize
at org.hibernate.util.SerializationHelper.deserialize(SerializationHelper.java:217)
at org.hibernate.util.SerializationHelper.deserialize(SerializationHelper.java:240)
at org.hibernate.type.SerializableType.fromBytes(SerializableType.java:82)
at org.hibernate.type.SerializableType.get(SerializableType.java:39)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
at org.hibernate.type.AbstractType.hydrate(AbstractType.java:81)
at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2096)
at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)
at org.hibernate.loader.Loader.getRow(Loader.java:1206)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580)
at org.hibernate.loader.Loader.doQuery(Loader.java:701)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3044)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:98)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:836)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:66)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
at hibernatetutor.tablebeans.Aaddress$$EnhancerByCGLIB$$44bec229.getStreet(<generated>)
at hibernatetutor.Main.getData(Main.java:33)
at hibernatetutor.Main.main(Main.java:24)
Caused by: java.io.StreamCorruptedException: invalid stream header
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:753)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:268)
at org.hibernate.util.SerializationHelper$CustomObjectInputStream.<init>(SerializationHelper.java:252)
at org.hibernate.util.SerializationHelper.deserialize(SerializationHelper.java:209)
... 29 more
On the basis of both the question, and some of the info from the comment section, I believe your troubles are caused by the following:
You have for some reason chosen the street attribute to be of type serializable. In your table this column has been defined as of type TEXT. Hibernate probably manages to save the serialized data to the column, but the database probably does not manage to keep them unaltered. Therefore, on retrieval, the now garbled serialized fail to deserialize.
The solution is, as Petr Pudlák noted, to get your mapping to be correct. If you choose a suitable binary type, such as BYTEA, then you will be able to store the binary data unaltered. The retrieval should then work.
This is not the right solution IMHO, which would be to choose a suitable data type in your java code in the first place. Having the type of street to be Serializable is confusing to anyone viewing your code. String would probably make more sense, and would also be a good fit for the column type TEXT.
Using a getter doesn't involve serialization unless you have some very unusual framework to do this.
I suggest you look at the exact stack trace (and post it in the question) and see where the exception is actually occurring.
I tried your classes, and it works for me:
import java.io.*;
class SampleClass implements Serializable {
String name;
Serializable fieldName;
}
class AnotherClass implements Serializable {
SampleClass sampleClass;
}
public class Ser {
public static void main(String argv[])
throws Exception
{
SampleClass s = new SampleClass();
s.name = "name";
s.fieldName = "fieldName";
AnotherClass a = new AnotherClass();
a.sampleClass = s;
// serialize the classes to a byte array
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(a);
oos.close();
// deserialize the classes from the byte array
ObjectInputStream is
= new ObjectInputStream(
new ByteArrayInputStream( os.toByteArray() ));
a = (AnotherClass)is.readObject();
is.close();
// print something
System.out.println(a.sampleClass.name);
}
}
Can you post the exact code that causes the problem?
I was doing simple things with hibernate, as I have to learn it for a project. I created this simple example:
package hibtests;
import hibtests.beans.newBean;
import org.hibernate.Session;
/**
*
* #author dario
*/
public class Main {
public void test(){
Session session = NewHibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
newBean nb = new newBean();
nb.setNome("FooFoo");
session.save(nb);
session.getTransaction().commit();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Main main = new Main();
main.test();
}
}
...and it was working fine, putting rows in the db. Then I worked on another class for a couple of hours. I try this example again and Hibernate makes this strange query:
Hibernate:
insert
into
TEST
(ID, NOME)
values
(default, ?)
Hibernate:
values
identity_val_local()
like it just can't read the property that is FooFoo. I checked if I changed the source... but it is not the case. Everything is just like before and there are not exceptions. The newBean instance is not null and FooFoo is in the Nome field. Why this?
Oh, I forgot, I'm using Netbeans 6.8 and JavaDB.
As requested, my mapping follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibtests.beans.newBean" table="APP.TEST">
<id name="id" column="ID">
<generator class="identity"/>
</id>
<property name="nome" column="NOME" type="string"/>
</class>
</hibernate-mapping>
Last minute update: turns out that insertion is working. Anyway I can still see the query with a ? instead of the string. Why?
As requested newbean source code follows:
public class newBean {
Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
String nome;
}
You will never see the value of the strings being inserted in the DB, you will always see them as question marks (?) there are sniffers that will show their contents, but in standard hibernate you will not see any values.