I am attempting to create a DTO object by persisting two classes using DataNucleus.
The DTO I wish to create:
#PersistenceAware
DtoObject{
Protected String Id; //populated by Order class
Protected String status; //populated by Order class
Protected String phoneNumber; //populated by Customer class
Protected String address; //populated by Customer class
}
The Objects:
#PersistenceCapable
#FetchGroup(name="dto", members = {#Persistent(name = "Id"),
#Persistent(name="status")})
public Class Order{
#PrimaryKey
#Persistent
private String Id;
#Persistent
private String status;
#Persistent
private Customer customer;
}
#PersistenceCapable
#FechGroup(name="dto", members = { #Persistent(name = "phoneNumber"),
#Peristent(name="address") })
public Class Customer{
#PrimaryKey
#Persistent
private String Id;
#Persistent
private String phoneNumber;
#Persistent
private string Address;
}
The JDODL:
Query q = pm.newQuery(Order.class);
pm.getFetchPlan().setGroup("dto");
q.setUnique(true);
q.setFilter("Id == id");
q.declareParameters("String id");
q.setResultClass(DtoObject.class);
DtoObject dto = (DtoObject)q.execute(id);
I can populate the dto object with its attributes mapped to the Order.class but can not get the attributes from the Customer.class. Data Nucleus joins the tables and selects the proper columns from each table but leaves the phoneNumber =null and address = null;
Pointers on how to make this work with one query will be appreciated.
Query q = pm.newQuery("SELECT UNIQUE this.id, this.status, " +
" this.customer.phoneNumber, this.customer.address INTO " +
DtoObject.class.getName() +
" FROM " + Order.class.getName() + " WHERE this.id = :id");
DtoObject dto = (DtoObject)q.execute(idParam);
Assuming the result class (DtoObject) obeys the conditions of a result class
Related
JPQL :
#Query(value="SELECT emp,CASE WHEN emp.country='india' THEN 'INDIAN' ELSE 'OTHER' END AS originCountry FROM EMPLOYEE emp") //originCountry is not a column of Entity/Table
List<Employee> findAllEmployee()
Now how to map originCountry to a java property/attribute ?
ALL result set attribute will be mapped to the employee object automatically but how to map the originCountry attribute as it is not coming from database table ?
Java class :
#Table(name="employee")
#Entity
#Getter
#Setter
class Employee{
#Id
#Column(name="emp_id")
private String empId;
#Column(name="emp_name")
private String empName;
#Column(name="emp_address")
private String empAddress;
#Transient
private String originCountry; //I want to map CASE statement alias result to this variable
}
Actually, #Airy's assumption is absolutely relevant!
I just have modeled the issue with a small h2-based project. Disclaimer: it's just a piece of code of a synthetic model
Given: Pet entity. It has a typeCode property of Integer.
Todo: Implement another property, that'll return pet's type name by its typeCode.
Here, we use #Transient annotation to specify that a given attribute should not be persisted, and #PostLoad annotation to make initTransient() method be invoked after entity loading. #Formula annotation is used to calculate a custom value for a typeName during entity loading
#Entity
public class Pet {
#Transient
public String ownerLastName;
#PostLoad
void initTransient() {
ownerLastName = Optional.ofNullable(getOwner()).map(Owner::getLastName).orElse("'Doe'");
}
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "r_owner_id", referencedColumnName = "id")
private Owner owner;
#Id
#Column(name = "id", nullable = false)
#GeneratedValue
private Long id;
#Column
private String name;
#Column
private Integer typeCode;
#Formula("case type_code when 1 then 'Cat' when 2 then 'Dog' else 'Chupacabra' end")
private String typeName;
//get, set etc
#Override
public String toString() {
return new StringJoiner(", ", Pet.class.getSimpleName() + "[", "]")
.add("id=" + getId())
.add("name='" + getName() + "'")
.add("typeCode=" + getTypeCode())
.add("ownerLastName=" + getOwnerLastName())
.add("typeName='" + getTypeName() + "'")
.toString();
}
}
In a bootstrap:
var cat = new Pet();
cat.setName("Oscar");
cat.setTypeCode(1);
var dog = new Pet();
dog.setName("Lucky");
dog.setTypeCode(2);
var customPet = new Pet();
customPet.setName("Smoking kills");
Set<Pet> pets = Set.of(cat, dog, customPet);
var owner = new Owner();
owner.setFirstName("John");
owner.setPets(pets);
ownerRepository.save(owner);
System.out.println("Database was successfully initialized with data: ");
System.out.println(cat);
System.out.println(owner);
Console:
Pet[id=1, name='Oscar', age=6, ownerLastName=null, typeCode=1, typeName='null', type=CAT]
Owner[id=1, lastName='null', firstName='John']
As you can see, ownerLastName=null here, but let's go on and load data.
test:
public void interact() {
var pet1 = petRepository.findById(1L).get();
var pet2 = petRepository.findById(2L).get();
var pet = petRepository.findById(3L).get();
var owner = ownerRepository.findById(1L).get();
System.out.println(pet1);
System.out.println(pet2);
System.out.println(pet);
System.out.println(owner);
}
Console:
Pet[id=1, name='Oscar', age=6, ownerLastName='Doe', typeCode=1, typeName='Cat', type=CAT]
Pet[id=2, name='Lucky', age=10, ownerLastName='Doe', typeCode=2, typeName='Dog', type=DOG]
Pet[id=3, name='Smoking kills', age=null, ownerLastName='Doe', typeCode=null, typeName='Chupacabra', type=null]
Owner[id=1, lastName='null', firstName='John']
As you can see, ownerLastName has been initialized with a default Doe value.
From Hibernate's reference of formula:
You can use a SQL fragment (aka formula) instead of mapping a property
into a column
Description:
I have two tables, Shop_Employee and Shop_Employee_Type. I wanna display typeName directly when show the employee detail information, but I don't want to config the relationship(OneToMany or ManyToOne) between this two entity. Because this will load all Shop_Employee_Type column's value, but these values are useless for me, I just need typeName of Shop_Employee_Type.
Below is my code, but it doesn't work.
ShopEmployeeType:
#Entity
#Data
//#DynamicUpdate
public class ShopEmployeeType {
#Id
private String typeId;
private String shopId;
private String typeName;
private Integer typeStatus;
private String typeDescription;
}
Shop_Employee:
#Entity
#Data
public class ShopEmployee {
#Id
private String employeeId;
private String shopId;
private String typeId;
private String name;
private String code;
private String phone;
private Integer status;
private String idcardNumber;
private String image;
//#Transient
private String typeName;
public ShopEmployee() {
}
}
Repository:
#Query(value = "select u.*,t.type_name from shop_employee u inner join shop_employee_type t on u.type_id=t.type_id", nativeQuery = true)
List<ShopEmployee> findAllData();
This could show typeName as I wished, but there is an error appears when I save a new entity Shop_Employee; If I add a #Transient for 'typeName', It could save successfully, but the value of 'typeName' is null when I query entity Shop_Employee.
Your query should return two Objects Shop_Employee and a String, so the return results should not be List<ShopEmployee> it should be :
#Query(value = "select u.*, t.type_name from shop_employee ...", nativeQuery = true)
List<Object[]> findAllData();
Then you can get the ShopEmployee using :
List<Object[]> list = findAllData();
for(Object[] obj : list){
ShopEmployee shopEmployee = (ShopEmployee) obj[0];
String type_name = (String) obj[1];
}
So, in ShopEmployee entity you don't need to use :
//#Transient
//private String typeName;
I have a (simplified) table structure that looks something like that:
customer table:
id name
-------------------
1 customer1
alias table:
customer_id alias
-------------------------------
1 customer one
1 customer uno
When I run the following query I easily get the list of aliases per customer:
select * from customer_alias where customer_id=1;
I would like to use this query in my hibernate to populate a list of type String. I tried using #Formula as follows:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#Column(name = "id")
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#Column(name="name")
private String name;
#Formula("(select alias from customer_alias where customer_id = id)")
private List<String> aliases;
// Getters, setters, etc...
}
It didn't work and I got this exception:
Could not determine type for: java.util.List, at table: customer, for columns: [org.hibernate.mapping.Formula( (select alias from customer_alias where customer_id = id) )]
Is there anyway to achieve this? Doesn't have to be with #Formula of course. Any reasonable way would be great.
Here is an SQLFiddle of my example
You could use #ElementCollection for having a List of related aliases without the need to map the whole entity:
#ElementCollection
#CollectionTable(name = "customer_alias", joinColumns = #JoinColumn(name = "customer_id") )
#Column(name = "alias")
private List<String> aliases;
See also:
Difference between #OneToMany and #ElementCollection?
I think you don't want to use OneToMany annotation as the second table is just a list of strings you want to find something more elegant that would not require me to create another entity.
You can use #ElementCollection as below:
#Entity
#Table(name="college")
class College implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="college_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int collegeId;
#Column(name="name")
private String collegeName;
#ElementCollection
#CollectionTable(name="student", joinColumns=#JoinColumn(name="college_id"))
#Column(name="student_name")
private Set<String> students;
public College() {
}
public Set<String> getStudents() {
return students;
}
public void setStudents(Set<String> students) {
this.students = students;
}
public int getCollegeId() {
return collegeId;
}
public void setCollegeId(int collegeId) {
this.collegeId = collegeId;
}
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
#Override
public String toString() {
return "College [collegeId=" + collegeId + ", collegeName=" + collegeName + ", students=" + students + "]";
}
}
I don't think #Formula annotation supports collection it can only be applied for single-valued properties. Can't say if if there exists any tweak.
I am working on a spring mvc app in which there are 2 entities, contact and location. Following are my contact and location models:
#Entity
#Table(name="Contact")
public class ContactModel {
#Id
#Column(name="contactid")
#GeneratedValue
private int contactId;
#Column(name="contactname")
private String contactName;
#Column(name="contactemail")
private String email;
#Column(name="contactphone")
private String phone;
#Column(name="locationid")
private int locationId;
}
Location model:
#Entity
#Table(name="Location")
public class LocationModel {
#Id
#Column(name="locationid")
#GeneratedValue
private int locationId;
#Column(name="locationname")
private String locationName;
#Column(name="locationdesc")
private String locationDescription;
#Column(name="type")
private String locationType;
#Column(name="address")
private String address;
#Column(name="city")
private String city;
#Column(name="state")
private String state;
#Column(name="district")
private String district;
#Column(name="lattitude")
private String lattitude;
#Column(name="longitude")
private String longitude;
}
In contact dao, I am getting contact list using below code:
Session session = sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(ContactModel.class);
criteria.addOrder(Order.asc("contactName"));
return criteria.list();
Now I need to show contact list on contact home page which will show contact name and its corresponding location name.
To show the contact list, I need to create join in contact and location tables. Is this the proper way? How can we do this in hibernate?
Also is it the proper way to add location entity in contact entity, or do I have to use location model in contact?
You have to relate your both entities with relation .I am assuming that ContactModel have One-To-One relation with LocationModel
Change your ContactModel to
#Entity
#Table(name="Contact")
public class ContactModel {
#Id
#Column(name="contactid")
#GeneratedValue
private int contactId;
#Column(name="contactname")
private String contactName;
#Column(name="contactemail")
private String email;
#Column(name="contactphone")
private String phone;
#OneToOne
#Column(name="locationid")
private LocationModel location;
}
and do the rest of thing same as you are doing select only the ContactModel List objects.
public List<ContactModel> allContactModel(){
Session session = sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(ContactModel.class);
criteria.addOrder(Order.asc("contactName"));
return criteria.list();
}
and get the value of location in your controller by iterating over list ContactModel and fetch the LocationModel object from ContactModel as you fetch normal variable by ClassName.Fieldname.Here It will Give you a LocationModel object
List<ContactModel> mylist=ContactModel.allContactModel();
for(ContactModel cm: mylist){
LocationModel lm=ContactModel.getLocationModel();
System.out.println(cm.getContactName+" location is "+lm.getLocationName);
}
Now you have the LocationModel, you can fetch its further values also.
You can also further enhance this onetoone relation by specifying fetchtype,cascadetype etc accoding to your requirements.
Note:Assuming that you are have getter and setter in your model
i think the right way is that the contact can have a location, now add a location object to your contact, something like this:
#Entity
#Table(name="Contact")
public class ContactModel {
...
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "locationid", nullable = false)
private LocationModel location;
// can be removed
#Column(name="locationid")
private int locationId;
...
}
if you have this you get the location object when you call a contact record
The following query throws the exception:
Query query = session.createQuery("from Associate as a order by a.username asc");
associates = query.list();
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [ca.mypkg.model.Associate#0]
If I create an entry in the database with id of 0 it works just fine. I don't really get it because I'm just trying to load all the entries in the db not just a specific one.
Similar questions I've found have been concerned with trying to load an object with a given ID I'm doing no such thing.
Associate class:
#Table(name = "user")
#XmlRootElement(name = "associate")
public class Associate implements Serializable {
private String username;
private String password;
private String firstName;
private String lastName;
private String userType;
private int id;
private String email;
private String isActive;
private Department dept;
private String lastUpdated;
private String associate_type;
// ...
#Id
#GeneratedValue
public int getId() {
return id;
}
#OneToOne
#JoinColumn(name = "dept")
public Department getDept() {
return dept;
}
From my experience this type of error message usually means it does not find joined entity by mentioned id, and not the entity requested in the query (Associate, in your case).
My guess is that Associate class contains a join entity which has primitive type primary key.