play framework 2 ebean #manytoone Column specified twice - java

I'm running to some problems with ebean (using play framework 2 version 2.2.1)
I have two classes:
my graph class:
public class Graph extends Model {
#Id
#Column(name="id")
private String id;
#Column(name="type")
private String type;
#OneToMany(mappedBy="valGraph", cascade=CascadeType.ALL)
private List<Val> valItems;
and my value class (with Val.graphId foreign key Graph.id):
public class Val extends Model
#Id
#Column(name="valId")
private String valId;
#Id
#Column(name="graphId")
private String graphId;
#Column(name="Key")
private String Key;
#Column(name="Value")
private String Value;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="graphId")
private Graph valGraph;
but when trying to save a new item i get this error:
javax.persistence.PersistenceException: ERROR executing DML bindLog[] error[Column 'graphId' specified twice]

After numerous searchers around the web I found this answer here - thanks to jtal!
Just to summaries the problem:
Using Ebean i have made a #ManyToOne entity that is not implemented in the database in anyway,
even more the join field, in my case
graphId
is a valid field that has values of its own.
when trying to join the column on that field, it will always fail because it creates this sql query:
SELECT
*
FROM
Val;
select
t0.valId c0,
t0.graphId c1,
t0.Key c2,
t0.Value c3,
t0.graphId c4 <---- notice this duplicate
from
graph_val t0
in order to solve this, i tell ebean not to use the second set of properties.
my new ebean element looks like this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="graphId", insertable = false, updatable = false)
private Graph valGraph;
and it works! =)

Related

One-To-Many relation in JPA DELETE operation is not working

The entity model and repository is given below.
Channel.java
public class Channel extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "channel_name")
private String channelName;
#Column(name = "channel_type")
private Integer channelType;
#Column(name = "seq_id")
private Integer seqId;
#Column(name = "channel_device_key")
private String channeldeviceKey;
}
UserRoomChannel.java
public class UserRoomChannel extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#OneToOne
#JoinColumn(name = "user_house_id")
private UserHouse userHouse;
#OneToOne
#JoinColumn(name = "room_id")
private Room room;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Channel> channels;
}
UserRoomChannelReposirtory.java
public interface UserRoomChannelRepository extends JpaRepository<UserRoomChannel, Long> {
#Query(value = "DELETE FROM user_room_channel_channels WHERE channels_id=?1", nativeQuery = true)
void deleteUserRoomChannelChannels(Long id);
}
I can save the data successfully. When data is saved through this a third table named user_room_channel_channels is created.
EX:
user_room_channel_id channels_id
1 1
1 2
But When I tried to delete with channels_id it give me the error
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance:.....
The native query what I write it execute from the command line.
But using JPA I can't do that.
Any help or any suggestion for resolving this issue?
A collection with cascade="all-delete-orphan" was no longer referenced
by the owning entity instance
is because before you delete your channels(and its children), you somehow do the following:
you load your UserRoomChannel along with its Channels children in the collection from the Database.
somewhere in your code you change the reference of the children collection : myUserRoomChannel.setChannels(newChannelCollections) or myUserRoomChannel.channels =new ChannelCollections();
and you try to delete the user with your repositorisory.
Hibernate who remembered having set the children collection with reference A to the User can find the collection anymore, because User.channels is now User.channels == B (with B being a new reference to your collection).
How to fix it:
just find the place where you are replacing your children collections and instead of:
myUserRoomChannel.setChannels(newChannelCollections), or
myUserRoomChannel.channels =new ChannelCollections(),
just do
myUserRoomChannel.getChannels().add/delete/clearYourChannels()
I just have done the below step and it works perfectly.
Reomve:
myUserRoomChannel.setChannels(channels)
Add
myUserRoomChannel.getChannels().removeAll(channels) and then
userRoomChannelRepository.save(myUserRoomChannel)

Spring Boot / JPA / mySQL - many to one relationship creates too many SQL queries

I have a simple spring boot rest app connected with mySQL db and I'm trying to optimize number of queries within simple function:
List<Message> messages = messagesRepository.findBySenderIdOrReceiverIdOrderByTimeDesc(senderId, receiverId);
MessagesRepository:
public interface MessagesRepository extends CrudRepository<Message, Long> {
List<Message> findBySenderIdOrReceiverIdOrderByTimeDesc(Long senderId, Long receiverId);
}
Message:
#Entity
#Table(name="s_messages")
public class Message implements Serializable
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Transient
private int internalId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="senderId", referencedColumnName = "id", updatable=false, insertable=false)
private ProfileLite sender;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="receiverId", referencedColumnName = "id", updatable=false, insertable=false)
private ProfileLite receiver;
#Column(columnDefinition="TEXT")
private String message;
private long time;
private MessageStatus status;
}
ProfileLite:
#Entity
#Table(name="s_profiles")
public class ProfileLite implements Comparable<ProfileLite>
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String nickname;
private String country;
private String thumb;
private Gender gender;
}
After executing method mentioned above, hibernate generates about 40 SQL's (based on 40 profiles) like this:
SQL Log - PasteBin
so first collecting messages and then for each message creates another sql to gather profile.
Is it any possibility to push hibernate to create just one simple sql instead of 40 like: select * from s_messages m join s_profiles s1 on m.sender_id = s1.id join s_profiles s2 m_receiver_id = s2.id ? (pseudo code)
Thanks!
This could be a n + 1 problem.
You can use a JOIN FETCH in your JPA query to fix this.
A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection. It effectively overrides the outer join and lazy declarations of the mapping file for associations and collections.
Update your JPA repository like so
public interface MessagesRepository extends CrudRepository<Message, Long> {
#Query("Select m from Message m join fetch m.sender ms join fetch m.receiver mr where ms.id = :senderId or mr.id = :receiverId order by m.time desc")
List<Message> findBySenderIdOrReceiverIdOrderByTimeDesc(Long senderId, Long receiverId);
}
For a more detailed explanation check out this answer.
PS: I havent tested the query.

efficiently loading collection of collections from database in hibernate

I have an web application with hibernate which manages data in multiple languages. Currently basically every request generates a shower of select statements on the languagetranslations. The models are roughly as following:
Data <1-1> Placeholder <1-many> languageTranslation <many-1> language
If I query for all/many Dataobjects, I see lots of single selects which select one languageTranslation for the placeholder. The SQL I optimally would want to generate:
SELECT * FROM data join placeholder join languagetranslation
WHERE data.placeholder_id = placeholder.id
AND languagetranslation.placeholder_id = placeholder.id
AND languagetranslation.language_id = ?
so that I get every data with placeholder with translation in one single call. The languagetranslations have an composite primary key of language_id and placeholder_id.
I have no HBM file, everything is managed with annotations. Modelcode (only relevant sections are shown):
#Entity
public class Data {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, optional = false)
#Fetch(FetchMode.JOIN)
private Placeholder content;
}
public class Placeholder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "primaryKey.placeholder", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#Fetch(FetchMode.JOIN)
private Set<LanguageTranslation> languageTranslations = new HashSet<>();
}
public class LanguageTranslation {
#EmbeddedId
private LanguageTranslationPK primaryKey = new LanguageTranslationPK();
#Type(type = "org.hibernate.type.StringClobType")
private String text;
}
#Embeddable
public class LanguageTranslationPK {
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private TextPlaceholder textPlaceholder;
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private Language language;
}
public class Language {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
I experimented with FetchType and FetchMode but couldn't generate the behavior I want, it always single selects for single languageTranslations.
I also tried multiple ways to query, criteria based, HQL, and raw SQL. My current raw SQL query is the following:
String sql_query = "select data.*, lt.* from Data as data join languagetranslation as lt on data.content_id = lt.textplaceholder_id";
Query q = getSession().createSQLQuery(sql_query).addEntity("data", Data.class).addJoin("data.content_id", "data.title").addJoin("lt", "data.content.languageTranslations").setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return q.list();
Am I doing something generally wrong here? How can I convince hibernate to get all entities in one single database call? Or is there some other methods to improve performance in my case (e.g. batch selecting)?
You may create proxy pojo which have your all entity variables with getter setter and constructor. then initialize this constructor in hibernate query so that you just get all needed data from database.
import com.proxy;
class userProxy{
private string name;
private string password;
private string address;
private int pincode;
private byte[] profilePic;
private int age;
public userProxy(string name,string password){
this.name = name;
this.password = password;
}
//Getter and setter of all variable...
}
Then use this constructor to Hibernate query like
select new com.proxy.userProxy(user.name,user.password) from usertable
Am I doing something generally wrong here?
No, you are not. That is how Hibernate works.
How can I convince hibernate to get all entities in one single database call
You have to use HQL or SQL query to do that. You do not need to have HBM file. It can be done through #NamedQueries / #NamedQuery annotation with list method.
There are many samples on Internet as example simple one:
http://www.mkyong.com/hibernate/hibernate-named-query-examples/

Hibernate #OneToMany causing multiple select statements

I am working on a Hibernate issue, which involves 2 separate Entity beans defined separately in their own classes:
Store
StoreServer
Note that a Store will have more than one StoreServer - hence the use of the #OneToMany annotation. Please see the code snippets as follows:
Store:
#Entity
#Table(name="Store")
public class Store implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = 5644190852867691168L;
#Id
#Column(name="STORE_NO", nullable=false)
private int storeNumber;
#Column(name="STORE_NAME", nullable=false)
private String storeName;
#Column(name="STORE_PHONE", nullable=false)
private String storePhone;
//other Store fields...
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name="STORE_NO", insertable=false, updatable=false)
private List<StoreServer> storeServers = new ArrayList<StoreServer>();
//getters and setters
StoreServer:
#Entity
#Table(name="Store_Server")
public class StoreServer implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -5410564578856243437L;
#Id
private StoreServerPK storeServerPK;
#Column(name="IP_ADDRESS", nullable=true)
private String ipAddress;
//other StoreServer fields...getters and setters
Since StoreServer has a composite Primary Key, here is StoreServerPK:
#Embeddable
public class StoreServerPK implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -1401889029390423604L;
#Column(name="STORE_NO", nullable=false)
protected int storeNumber;
#Column(name="SERVER_NO", nullable=false)
protected String serverNumber;
//getters and setters
At present, I am getting the correct results, but the performance is unacceptably SLOW. I have switched on logging in Hibernate and I can see that a separate SELECT query is being run for each Store Entity in order to obtain the associated StoreServer records.
Currently, in the logs, I see a single SELECT statement to obtain the Store records (more than 200 results returned). Then for each store, a new SELECT statement to get the StoreServer records. My question is...Why is Hibernate not doing a join (running one query)?
Please could I get some help on how to tell Hibernate to run a single query, using a JOIN?
Thank you
It is called N+1 problem
The solution actually depends on how do you make your query - in case if you are using Criteria API you should use Root.fetch method:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Store> cq = qb.createQuery(Store.class);
Root<Store> root = cq.from(Store.class);
root.fetch(App_.storeServers, JoinType.LEFT);
cq.select(root);
return em.createQuery(cq).getResultList();
If you are using HQL you should use fetch keyword:
select distinct st from Store st left join fetch st.storeServers
It might be a good idea to validate the number of queries generated by Hibernate in your unit tests using in-memory database like H2 and JDBC Sniffer

Why in this Hibernate mapping it is used #ManyToOne instead #OneToOne?

I am absolutly new in Hibernate development and I have the following problem.
I have 2 entity classes that maps 2 DB tables:
1) The first entity class (the main one) is named KM_ProjectInfo and map a DB table named KM_PROJECT.
2) The second entity class is named KM_ProjectInfoStatus and map a DB table named KM_PROJECT_INFO_STATUS.
So the second one represent a specific field of the first one (a status of the row representd by an instance of the KM_ProjectInfo class). Infact I have something like this:
1) KM_ProjectInfo class:
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#Column(name = "name")
private String name;
#Column(name = "technology")
private String technology;
#ManyToOne
#JoinColumn(name = "idCountry")
private KMCountry country;
#Column(name = "power")
private long power;
#Column(name = "cod")
private String cod;
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
// GETTERS & SETTERS
}
2) KM_ProjectInfoStatus:
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
// GETTERS & SETTERS
}
So, as you can see in the previous snippet, the KM_ProjectInfoStatuss is a field of the KM_ProjectInfo because I want that it contains the primary key of this table as foreign key.
In the logic of my application I want that at one row of the KM_PROJECT table (so at one instance of the KM_ProjectInfo entity class) is associated a single row of the KM_PROJECT_INFO_STATUS (one instance of the KM_ProjectInfoStatus entity class) because it represent a specific status for the KM_PROJECT row.
In my code I have:
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
but I think that is wrong because at one row of my first table it is associated a specific single row of the second table. But maybe I am missing something about how Hibernate work.
Can you help me to understand what I am missing? What it work? Why I have #ManyToOne instead #OneToOne?
Tnx
It all depends on how you want to model things. In terms of Database structure, OneToOne and ManyToOne are implemented in the same way:
One or more JoinColumns which makes a foreign key pointing to the primary key of the other table.
So both solutions correctly map to your database, but it depends if you want to allow several KM_ProjectInfo to point to the same KM_ProjectInfoStatus, or only allow a single one.
Note that, even though you would declare a OneToOne, you could still end up with multiple KM_ProjectInfo pointing to the same KM_ProjectInfoStatus if you don't manipulate Hibernate properly.
Here you did not declare the reverse relationship, but if you did, the declaration would have to be different:
In case of a OneToOne, you would have a KM_ProjectInfo member
In case of a OneToMany (reverse of ManyToOne), you would have a Collection<KM_ProjectInfo> member
From the description it seems you want to have one-to-one relationship. That is the project entity should have its very own status not shared by any other project. You could achieve this by using #OneToOne as below.
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#OneToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
}
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#OneToOne(mappedBy="idProjectInfoStatus")
private KM_ProjectInfo project;
}
This way you can have specific status for the KM_PROJECT.
Coming back to #ManyToOne, you will want to have this if you want to share the same status with multiple projects, but that's not what you want in your case. I have tried to explain mappings in simple way here One-to-One mapping.

Categories

Resources