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
Related
I am writing an order.hbm.xml file for my POJO class as below using Hibernate 3.0
import java.util.Set;
public class OrderDAO
{
private EmbeddedCustDAO embedCustID;
private Set<String> custOrderSet;
private String totalAmount;
public EmbeddedCustDAO getEmbedCustID()
{
return embedCustID;
}
public void setEmbedCustID(EmbeddedCustDAO embedCustID)
{
this.embedCustID = embedCustID;
}
public Set<String> getCustOrderSet()
{
return custOrderSet;
}
public void setCustOrderSet(Set<String> custOrderSet)
{
this.custOrderSet = custOrderSet;
}
public String getTotalAmount()
{
return totalAmount;
}
public void setTotalAmount(String totalAmount)
{
this.totalAmount = totalAmount;
}
}
The Embedded/ Composite ID class is,
public class EmbeddedCustDAO
{
private String customerName;
private String custAddress;
public String getCustomerName()
{
return customerName;
}
public void setCustomerName(String customerName)
{
this.customerName = customerName;
}
public String getCustAddress()
{
return custAddress;
}
public void setCustAddress(String custAddress)
{
this.custAddress = custAddress;
}
}
The order.hbm.xml file that I have written is,
<?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="org.harish.dao.OrderDAO"
table="order_table">
<composite-id name="embedCustId"
class="org.harish.dao.EmbeddedCustDAO">
<key-property name="customerName"/>
<key-property name="custAddress" />
</composite-id>
<set name="custOrderSet">
<key column="??" />
<element type="string" column="??"/>
</set>
<property name="totalAmount" />
</class>
</hibernate-mapping>
I am not sure how to declare the set in the order.hbm.xml above.
I went through the Hibernate 3.0 Reference. It mentions the key column in the Set to be column name of the Primary Key defined in the .hbm.xml. But, with a composite ID what would be the Column name?
Is a one-to-many mapping required here for the Set?
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 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.
Here is the code I am using:
Database:
CREATE TABLE IF NOT EXISTS `btech_faculty_assigned` (
`subject_id` varchar(8) NOT NULL,
`year` varchar(4) NOT NULL,
`section` varchar(1) NOT NULL,
`branch` varchar(10) NOT NULL,
`semister` varchar(1) NOT NULL,
`FID` varchar(10) NOT NULL,
`islab` varchar(1) NOT NULL,
PRIMARY KEY (`subject_id`,`year`,`section`,`branch`,`semister`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
hbm file:
<?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="com.att_marks.students.BtechFacultyAssigned" table="btech_faculty_assigned">
<composite-id>
<key-property name="subjectID" column="subject_id"/>
<key-property name="year" column="year"/>
<key-property name="section" column="section"/>
<key-property name="branch" column="branch"/>
<key-property name="semister" column="semister"/>
</composite-id>
<property name="FID" column="FID"></property>
<property name="islab" column="islab"></property>
</class>
</hibernate-mapping>
POJO file:
package com.att_marks.students;
public class BtechFacultyAssigned {
private String subjectID;
public String getSubjectID() {
return subjectID;
}
public void setSubjectID(String subjectID) {
this.subjectID = subjectID;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public String getSeciton() {
return section;
}
public void setSeciton(String section) {
this.section = section;
}
public String getFID() {
return FID;
}
public void setFID(String fID) {
FID = fID;
}
public String getSemister() {
return semister;
}
public void setSemister(String semister) {
this.semister = semister;
}
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public String getIslab() {
return islab;
}
public void setIslab(String islab) {
this.islab = islab;
}
private String year;
private String section;
private String FID;
private String semister;
private String branch;
private String islab;
}
The entire project works fine as long as this hbm file is not included in the configuration file. The moment this file is included in the configuration file, the sessionFactory.createQuery(query) file throws null pointer exception.
The Hibernate doc said:
A table with a composite key can be
mapped with multiple properties of the
class as identifier properties. The
element accepts
property mappings and
mappings as child
elements.
The persistent class must override
equals() and hashCode() to implement
composite identifier equality. It must
also implement Serializable.
Even if this is maybe not the cause of your problem (but the behavior looks like), you have to follow this requirements and add equals, hashcode and serilizable.
I am using hibernate3 in my java app to access sqlserver 2008 enterprise.
The hibernate mapping uses composite id and when i try to load model it returns null. I spent days to resolve it but still no result. Composite id mapping should be used for multiple field based PK, but in my table no such PK, i wonder why the JBoss Hibernate Tool(eclipse plugin) generated it with composite id mapping ?
I would appreciate any help or comments.
Hibernate Models:
public class AuthorLoginTrack implements java.io.Serializable {
private AuthorLoginTrackId id;
public AuthorLoginTrack() {
}
public AuthorLoginTrack(AuthorLoginTrackId id) {
this.id = id;
}
public AuthorLoginTrackId getId() {
return this.id;
}
public void setId(AuthorLoginTrackId id) {
this.id = id;
}
}
public class AuthorLoginTrackId implements java.io.Serializable {
private long id;
private String userId;
private Date dateCreated;
public AuthorLoginTrackId() {
}
public AuthorLoginTrackId(long id, String userId) {
this.id = id;
this.userId = userId;
}
public AuthorLoginTrackId(long id, String userId, Date dateCreated) {
this.id = id;
this.userId = userId;
this.dateCreated = dateCreated;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getUserId() {
return this.userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Date getDateCreated() {
return this.dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof AuthorLoginTrackId))
return false;
AuthorLoginTrackId castOther = (AuthorLoginTrackId) other;
return (this.getId() == castOther.getId())
&& ((this.getUserId() == castOther.getUserId()) || (this
.getUserId() != null
&& castOther.getUserId() != null && this.getUserId()
.equals(castOther.getUserId())))
&& ((this.getDateCreated() == castOther.getDateCreated()) || (this
.getDateCreated() != null
&& castOther.getDateCreated() != null && this
.getDateCreated().equals(castOther.getDateCreated())));
}
public int hashCode() {
int result = 17;
result = 37 * result + (int) this.getId();
result = 37 * result
+ (getUserId() == null ? 0 : this.getUserId().hashCode());
result = 37
* result
+ (getDateCreated() == null ? 0 : this.getDateCreated()
.hashCode());
return result;
}
}
Hibernate Mapping:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Apr 6, 2010 4:17:46 PM by Hibernate Tools 3.3.0.GA -->
<hibernate-mapping>
<class name="com.entity.model.AuthorLoginTrack" table="AuthorLoginTrack" schema="dbo" catalog="tribetoyota_db">
<composite-id name="id" class="com.entity.model.AuthorLoginTrackId">
<key-property name="id" type="long">
<column name="ID" precision="18" scale="0" />
</key-property>
<key-property name="userId" type="string">
<column name="UserID" length="20" />
</key-property>
<key-property name="dateCreated" type="timestamp">
<column name="DateCreated" length="16" />
</key-property>
</composite-id>
</class>
</hibernate-mapping>
Table Dump:
/****** Object: Table [dbo].[AuthorLoginTrack] Script Date: 04/14/2010 20:43:02 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[AuthorLoginTrack](
[ID] [numeric](18, 0) IDENTITY(1,1) NOT NULL,
[UserID] [varchar](20) NOT NULL,
[DateCreated] [smalldatetime] NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[AuthorLoginTrack] ADD CONSTRAINT [DF_AuthorLoginTrack_DateCreated] DEFAULT (getdate()) FOR [DateCreated]
GO
Table:
ID UserID DateCreated
------------------------------------
5 cooler 2005-03-17 18:56:00
6 miumiu 2005-03-17 19:46:00
DAO Code:
AuthorLoginTrack track;
AuthorLoginTrackId trackId;
trackId = new AuthorLoginTrackId();
trackId.setId(5);
track = (AuthorLoginTrack)getSession().load(AuthorLoginTrack.class, trackId);
return track.getId().getUserId(); // returns null why ?:((
Session.load(...) assumes that there's indeed an instance with the given id, most of the time it will return a proxy object without hitting the database. What you're really querying is an instance with id 5, userId = null and date == null.
Basically you're getting an unitialized proxy with a copy of the composite id you used to query it. If the instance really exists you'll be fine, otherwise you'll get an ObjectNotFoundException the first time you'll try to use the object.