How to properly map MyBatis parent relation? - oracle

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!

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>

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>

Using finders in views

I don't understand how I can use a finder that takes a parameter to display only the returned data.
For example, I have a finder findPrincipalsByUsernameLike(String username) in Principle entity, how can I use this finder in list.jspx to show the user only his/her data?
Principle.java
#RooJavaBean
#RooToString
#RooJpaActiveRecord(table = "security_principals", finders = { "findPrincipalsByUsernameLike" })
public class Principal {
#NotNull
#Size(min = 5, max = 50)
private String username;
#NotNull
#Size(min = 5, max = 50)
private String password;
private Boolean enabled;
}
users/list.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:page="urn:jsptagdir:/WEB-INF/tags/form"
xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<jsp:output omit-xml-declaration="yes" />
<page:find finderName="findPrincipalsByUsernameLike"
id="ff_edu_gju_syria_model_security_principal"
path="/principals/find">
<input data-dojo-props="trim:true" data-dojo-type="dijit/form/TextBox"
id="toolbarFilter" name="filter" type="text" value="${filter}" />
</page:find>
<page:list id="pl_edu_gju_syria_model_security_Principal"
items="${principals}" z="+E0uiC5dOFGeuICZbYdoS6Nz80o=">
<table:table data="${principals}"
id="l_edu_gju_syria_model_security_Principal" path="/security/users"
z="9ue3QOERNh44QuGQIg9JQzmFKnI=">
<table:column id="c_edu_gju_syria_model_security_Principal_username"
property="username" z="REKFqtjHv0gJUiSxe+y1TKytm1w=" />
<table:column id="c_edu_gju_syria_model_security_Principal_password"
property="password" z="4khHEC8FhwrPHVFPG7c4s5cP8L4=" />
<table:column id="c_edu_gju_syria_model_security_Principal_enabled"
property="enabled" z="boq7MuGPymSQN1CCWrQ8INhy1DM=" />
</table:table>
</page:list>
</div>
How can I pass the current username without showing a text area for the user?
You don't have to send the user as request param.
In the Controller method that handles the request just get the user principal from the request.
Spring Security 3.2 introduced great improvements for easier user detail management, take a look to How to get active user's UserDetails

JPA | One to one mapping with eager fetch

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>

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