JPA | One to one mapping with eager fetch - spring

I am working with a JPA sample application with hibernate as JPA provider.
In which I am having a one to one mapping with fetch type EAGER.
Here below is my code block:
Person.java
public class Person extends Persistent implements Serializable {
private static final long serialVersionUID = 1L;
String name;
// address as composition
Address address;
String addressId;
Address.java
public class Address extends Persistent implements Serializable {
private static final long serialVersionUID = 1L;
String address;
mapping.xml
<entity name="Person" class="com.csam.mtp.wallet.wallet.Person"
access="FIELD">
<table name="person" />
<attributes>
<basic name="name">
<column name="name" length="36" nullable="false" />
</basic>
<basic name="addressid">
<column name="addressid" length="36"/>
</basic>
<one-to-one name="address" fetch="EAGER">
<join-column name="addressid"
referenced-column-name="id" insertable="false" updatable="false"
nullable="false" />
</one-to-one>
</attributes>
</entity>
<entity name="address" class="com.csam.mtp.wallet.wallet.Address"
access="FIELD">
<table name="address" />
<attributes>
<basic name="address">
<column name="address" length="36" />
</basic>
</attributes>
</entity>
DB Script
CREATE TABLE address (
id VARCHAR (50) not null,
address VARCHAR(50),
primary key(id)
);
CREATE TABLE person (
id VARCHAR (50) not null,
name VARCHAR(50),
addressid VARCHAR(50),
primary key(id),
foreign key (addressid) REFERENCES address(id),
);
Driver class
Address address = new Address();
address.setAddress("test");
address = addressRepository.save(address);
Person person = new Person("Mike");
//person.setAddressId(address.id());
try {
person = personRepository.save(person);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(personRepository.exists(person.getID()));
Person savedPerson = personRepository.findOne(person.getID());
// Asserts
assertNotNull(savedPerson);
assertNotNull(savedPerson.id());
While saving person If i don't set address id then it saves person successfully, exists returns true but find one api always returns null
while saving person if first I save address and set address id in person and save it then
It saves person successfully, exists return true and find one api return Person object with address object (as it is eager)
So here below is my query,
Is it in case of one to one mapping with eager fetch on child, child must be saved and set in parent before the saving of parent?
I tried to find out related arcticle but not able to get one, Please assist.
Thanks in advance

In your case the Address is the parent and the Person is the Child. That's because the Child points to the Parent through the addressId FK.
Because you don't have a bi-directional association, you shouldn't have insertable="false" updatable="false"
With these settings, Hibernate won't be able to include the addressId in the INSERT or the UPDATE query.
Remove insertable="false" updatable="false"
Remove the addressId column too, since you can reference it through address.id
Add cascade from Person to Address:
<one-to-one name="address" fetch="EAGER">
<join-column name="addressid"
referenced-column-name="id" nullable="false" />
<cascade>
<cascade-persist/>
<cascade-merge/>
</cascade>
</one-to-one>
Probably it would be better to have a bi-directional association so that an Address has also a reference to a Person and in that case you should probably have a one-to-many association, since an Address may have multiple Persons.

Finally got the solution with nullable=true, as below:
<one-to-one name="address" fetch="EAGER">
<join-column name="addressid"
referenced-column-name="id" insertable="false" updatable="false"
nullable="true" />
</one-to-one>

Related

How to fill outer and inner classes

import lombok.Data;
#Data
public class P {
private String name;
private String operationId;
#Data
public static class Operation {
private Timestamp ts;
private String id;
}
}
I want to fill in the fields of both classes with one query using join
<select id="getAllP" resultType="com.mappers.P">
SELECT t."name" as name, t."operationId" as operationId, o."ts" as ts, o."id" as id
FROM FROM "P" t JOIN "Operation" o ON t."operationId" = o."id"
</select>
If leave resultType = "com.mappers.P" in this form, then only the field of the outer class is filled, if resultType = "com.mappers.P & Operation" then only the internal one, how to ensure that the fields of both classes are filled? thanks in advance
Something like this, although you can go deeper by splitting them into two result maps and building them that way. Use the documentation above, and you can get super creative.
<resultMap id="pMap">
<result property="name" column="name"/>
<association type="Operation" columnPrefix="o_">
<result property="name" column="name"/>
<result property="ts" column="name"/>
</association>
</resultMap>
<select id="getAllP" resultMap="pMap">
...
</select>

How to properly map MyBatis parent relation?

I'm currently struggling on trying to map my class using MyBatis and Oracle database.
I have a table with columns ID, CODE, DESCRIPTION, PARENT, VALUE, UNITS
I have a model:
public class Parameter {
private String code;
private String description;
private String value;
private Parameter parent;
private String units;
<...>
}
and a xml mapper parameterMapper.xml:
<resultMap id="ParameterResultMap" type="Parameter">
<id property="id" column="id" />
<result property="code" column="code" />
<result property="description" column="description" />
<result property="value" column="value" />
<result property="units" column="units" />
<association property="parent"
resultMap="persistence.ParameterMapper.ParameterResultMap"
columnPrefix="parent_" />
</resultMap>
<select id="readParameter" parameterType="Parameter" resultMap="ParameterResultMap">
SELECT
p.id,
p.code,
p.description,
p.value,
p.units,
par.id AS parent_id,
par.code AS parent_code,
par.description AS parent_description,
par.value AS parent_value,
par.units AS parent_units
FROM parameter p
LEFT JOIN parameter par ON par.code = p.parent
</select>
And I get java.lang.IllegalArgumentException: Result Maps collection does not contain value for persistence.ParameterMapper.ParameterResultMap] exception on my console.
What I've done wrong and where could be the problem?
Thanks in advance!

DBUnit error : NoSuchTableException

I tried to write Unit tests for my database Services according to this example (https://github.com/21decemb/spring-boot-dbunit-example). I created dataset and test example. After I run the test I recived: org.dbunit.dataset.NoSuchTableException: orders
dataset.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<!-- CUSTOMER DATA -->
<customers id="1" name="Customer" active="1"/>
<!-- POSITION DATA -->
<positions id="1" name="POSITION1"/>
<positions id="2" name="POSITION2"/>
<positions id="3" name="POSITION3"/>
<!-- ORDER DATA -->
<orders id="1" name="order1" color="RED" express="0" date="2016-12-11 19:47:39" last_update="2016-12-11 19:47:39" parent_id="1" active="1"/>
<!--<orders id="2" name="order2" color="WHITE" customer_id="1" position_id="1" express="0" date="2016-12-11 19:47:39" last_update="2016-12-11 19:47:39" active="0"/>-->
</dataset>
The second Order Row is commented becacuse I was testing two possibilities. I know that this is because of joins. When I test only 'positions' and 'customers' (simple entities without joins) it works correctly.
My "Order" entity:
#Entity
#Table(name = "orders", schema = Config.dbSchema)
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="POSITION_ID")
private Position position;
private short express;
private Date date;
#Column(name="LAST_UPDATE")
private Date lastUpdate;
#Column(name="parent_id")
private Long parentId;
private short active;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="CUSTOMER_ID")
private Customer customer;
#OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<Component> components;
//getters and setters
}
Have anyone any idea on how to fix it?
Thanks in advance.
You have to add this property in the override method setUpDatabaseConfig:
#Override
protected void setUpDatabaseConfig(DatabaseConfig config) {
config.setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true);
}
In addition probably you need to add the schema name like this:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<!-- CUSTOMER DATA -->
<schemaName customers id="1" name="Customer" active="1"/>
<!-- POSITION DATA -->
<schemaName positions id="1" name="POSITION1"/>
<schemaName positions id="2" name="POSITION2"/>
<schemaName positions id="3" name="POSITION3"/>
<!-- ORDER DATA -->
<schemaName orders id="1" name="order1" color="RED" express="0" date="2016-12-11 19:47:39" last_update="2016-12-11 19:47:39" parent_id="1" active="1"/>
<!--<schemaName orders id="2" name="order2" color="WHITE" customer_id="1" position_id="1" express="0" date="2016-12-11 19:47:39" last_update="2016-12-11 19:47:39" active="0"/>-->
</dataset>

org.hibernate.exception.SQLGrammarException: could not get next sequence value and sequence does not exit

I'm using oracle sql developer and create a table named avatar. It has two columns , avatarid and image .
I also create a trigger and sequence for id auto generation .
But when I use this table with hibernate and spring tool suite , I got an exception ,that is
org.hibernate.exception.SQLGrammarException: could not get next
sequence value.
It says
sequence does not exit
I try all the answers but can't fix the error. In Sprig Tool Suite , I connect to database using datasource and search my sequence under my schema , and my sequence doesn't exit.
I created the sequence along with trigger using Oracle SQL Developer.
Please give me some more advice.
Here is my Avatar.hbm.xml
<hibernate-mapping>
<class name="com.bluestone.fileupload.model.Avatar" table="avatar">
<id name="avatarid" type="int">
<column name="AVATARID" />
<generator class="sequence">
<param name="my_seq">SU_AVATAR_SEQUENCE</param>
</generator>
</id>
<property name="image" type="binary">
<column name="IMAGE" not-null="true" />
</property>
</class>
</hibernate-mapping>
And , this is my Avatar.class
#Entity
#Table(name = "avatar")
public class Avatar{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
private int avatarid;
private byte[] image;
public Avatar() {
}
public Avatar(int avatarid, byte[] image) {
super();
this.avatarid = avatarid;
this.image = image;
}
public void setAvatarid(int avatarid) {
this.avatarid = avatarid;
}
.
.
1) Make sure is the sequence is available in the database
2) May be you need to use schema say eg : HR.SU_AVATAR_SEQUENCE
where HR is the schema name
3)Else create sysnonym for schema
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "my_seq_gen_9")
#SequenceGenerator(name = "my_seq_gen_9", sequenceName = "SEQ_NAME_YOUR")
#Column(name = "COLUMN")
private long columnName;
Sequence can used as in hibernate.
I faced the same issue.
My Resolution was:
#SequenceGenerator(name = "ID_GENERATOR", sequenceName = "ACTIVITY_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_GENERATOR")

How to add Restrictions to Criteria API in Hibernate

I am newbie to Hibernate and Spring. My question is on “ how to add criteria…” in situation like, I have two beans:
1) Language:
public class Language {
private int languageId;
private String languageName;
private List<Topic> listOfAllTopics;
}
2) Topic:
public class Topic {
private int topicId;
private String topicName;
}
My database tables:
1) language
language_id int(PK)
language_name varchar(30)
2) topic
topic_id int(PK)
topic_name varchar(30)
language_id int(FK)
Hibernate mapping xml files are:
1) Language.hbm.xml
<hibernate-mapping package="com.mw.javamentordb.dto">
<class name="Language" table="language" lazy="true">
<id name="languageId" column="language_id" type="int">
<generator class="increment" />
</id>
<property name="languageName" column="language_name"type="string"/>
<bag name="listOfAllTopics" cascade="all">
<key column="language_id" not-null="true" />
<one-to-many class="Topic"/>
</bag>
</class>
</hibernate-mapping>
2) Topic.hbm.xml
<hibernate-mapping package="com.mw.javamentordb.dto">
<class name="Topic" table="topics" lazy="true">
<id name="topicId" column="topics_id" type="int">
<generator class="increment" />
</id>
<property name="topicName" column="topics_name" type="string" />
</class>
</hibernate-mapping>
And I get all lang in my database using this method and it works properly.
public List<Language> getAllLanguages() {
Criteria criteria = getSession().createCriteria(Language.class);
return criteria.list();
}
But when I try to get all topics of particular language(by langId) using criteria, it not works.
Actually I don’t know how to use criteria in such kind of situation..
public List<Topic> getAllTopicOfLanguage(Language language) {
Criteria criteria = getSession().createCriteria(Topic.class);
criteria.add(Restrictions.eq("?");
return criteria.list();
}
Your table cheme for Topic has a foreign key constraint language_id but your class and hbm.xml mapping does not.
So your desired query is not possible.
Change it to:
public class Topic
{
private int topicId;
private String topicName;
private Language language;
}
And at the property to hbm.xml:
<many-to-one name="language" class="package.Language" fetch="select">
<column name="language_id">
</many-to-one>
Then you can query it using criteria like following:
criteria.add(Expression.eq("language.language_id", language.getLanguageId()));
Alternatively you could use the equality on the object itself instead their id's or use Expression.eqId(Object Object)
Advice:
Use a abstract superclass with the identifier field to make things more generic. Naming the identifier once topicId on class and table Topic and languageId on class and table Language just is overhead. Just use id on property, class and table to make things easier.
In larger applications this will become more obvious.
you are searching your result through wrong way, as per your design, you will get the result through,
public Language getAllTopicOfLanguage(Language language) {
Criteria criteria = getSession().createCriteria(Language.class);
criteria.add(Expression.eq("languageId",language.getLanguageId()));
return (Language)(criteria.list().get(0));
}
this will return you Language DTO object, with list of topics matching the language id filled in Language DTO only.
as you are newbie, just want to tell you that when you are using hibernate, your DTO and table design should be very precise.

Resources