I've got this entity:
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "offer_categs")
public class OfferCateg {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Nullable
private String image;
#ManyToOne
#JoinColumn(name = "parent_id")
private OfferCateg parent;
#OneToMany(mappedBy = "parent",
cascade = CascadeType.REMOVE,
fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<OfferCateg> children;
}
I'm trying to just create categories and subcategories. My problem is that when using the below method, parent_id might be null in case of a root category. How can I make the query to allow for that? I can't use IS NULL. Should I make two queries and be careful of the calling parent_id value ?
#Query(value = "SELECT * FROM offer_categs oc WHERE oc.parent_id = ?1 AND oc.name = ?2", nativeQuery = true)
OfferCateg findByNameAndParent(#Nullable Long parentId, String name);
EDIT
I changed the query to this and it seems to be working
#Query(value = "SELECT * FROM offer_categs oc WHERE " +
"(oc.parent_id = ?1 AND oc.name = ?2) OR" +
"(oc.parent_id IS NULL AND ?1 IS NULL AND oc.name = ?2)",
nativeQuery = true)
OfferCateg findByNameAndParent(#Nullable Long parentId, String name);
BUT...isn't this overkill ?! I'm sure it can be done more easily.
Related
I have two entities User:
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userID;
#Column(name = "userHashedPassword")
private String password;
#Column(name = "userName")
private String userName;
#Column(name = "userEmail")
private String email;
#Transient
private List<String> groups = new LinkedList<>();
#ManyToMany
#JoinTable(name = "UserRoles",
joinColumns = #JoinColumn(
name = "userID"),
inverseJoinColumns = #JoinColumn(
name = "roleID"))
private Set<Role> roles = new HashSet<>();
#OneToMany(mappedBy = "user")
private Set<Rating> ratings;
protected User(){}
public User(String userHashedPassword, String userName, String email, Set<Role> roles){
this.password = userHashedPassword;
this.userName = userName;
this.email = email;
this.roles = roles;
}
//getters and setters
}
And Group:
#Table(name="FocusGroups")
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "groupID")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long groupID;
private String groupName;
#ManyToMany
#JoinTable(name = "GroupMembers",
joinColumns = #JoinColumn(
name = "groupID"),
inverseJoinColumns = #JoinColumn(
name = "userID"))
private Set<User> groupMembers = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name="frameworkID", nullable = true)
private Framework framework;
public Group(){}
public Group(String groupName, Set<User> groupMembers, Framework framework) {
this.groupName = groupName;
this.groupMembers = groupMembers;
this.framework = framework;
}
//getters setters
}
When I delete a User, I want to remove them from group members, however it fails due to foreign key constraint: java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (capripol.groupmembers, CONSTRAINT FK98tbu0sjfsn1m5p340dn0v8wo FOREIGN KEY (userID) REFERENCES users (userID))
How do I work around this?
Well, I will try to answer: First of all, it is rather strange you refer on Groups in user
entity like that:
#Transient
private List<String> groups = new LinkedList<>();
It this case, you will not have a column group in user table in database, hence you have to first perform removal from group_members for an all groups:
delete from group_members where userid = <user_id_you_want_to_remove>;
And only after your JoinTable table does not contain any refers to user with <user_id_you_want_to_remove>, than you can execute
delete from users where userid = 1;
Note: there is no matter you do it by spring data (e.g. deleteById(Long id) and using #Query annotation specify the query above in SQL or HQL, up to you) - this will work. But I highly recommend you to reconsider you database structure - it is not cute to store only one entity.
I have a Folder entity in Hibernate, like so:
#Entity
#Table(name = "folders")
public class Folder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "folder_id", unique = true, nullable = false)
private int id;
#NonNull
#Column(name = "name", length = 100, unique = true, nullable = false)
private String name;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
#Column(name = "sub_folders")
private Set<Folder> childFolders = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonIgnore
#JoinColumn(name = "parent", referencedColumnName = "folder_id", nullable = true)
private Folder parent;
public Folder() {
}
}
I'm trying to write a finder method or custom query which will do what I wrote in the subject.
So if I send a request going like folders/{parent_folder_id}, let's say value being 1, I should get objects 4 and 5, but without their children, so not including 6,7,8 and 9.
Ideally, hibernate query would be preferred. If not, any sql language is also fine. I'll try to tumble it up to hibernate.
This is what I got, I still get children...
#Query(value = "Select * from folders f where f.parent = ?1 ", nativeQuery = true)
Set<Folder> getFolders(int folder_id);
I think this should work:
make the default fetchtype lazy:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
#Column(name = "sub_folders")
private Set<Folder> childFolders = new HashSet<>();
Use a JOIN FETCH in order to eagerly fetch the relationships you want.
SELECT f FROM folders f JOIN FETCH f.childFolders
You probably can achieve something similar with entity graphs but I'm not sure about their interaction with queries.
I got what I need with following query:
#Query(value = "Select folder_id, name, parent From folders f Where f.parent = ?1", nativeQuery = true)
This will give me just name of the folder and its Id.
I would like write this query into HQL :
select DISTINCT * from transportation transp
inner join price p on p.transportationId = transp.transportationId
where p.sectionId = ( select sec.sectionId from section sec where sec.lineId = ( select l.lineId from line l where l.lineId = 1000000000) )
But don't know write subqueries. I know I must use DetachedCriteria.
An other question:
If we can write this query in native query (using createSQLQuery), how can cast returned objet to Transportation entity ?
Thanks
My entities :
#Entity
#Table(name = "transportation")
public class Transportation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "transportationId")
private Integer id;
#OneToMany(mappedBy = "transportation", fetch = FetchType.EAGER)
#JsonBackReference(value = "price-transportation")
private Set<Price> prices = new HashSet<Price>();
...
}
#Entity
#Table(name = "price")
public class Price implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "priceId")
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "transportationId")
#JsonManagedReference(value = "price-transportation")
private Transportation transportation;
...
}
#Entity
#Table(name = "section")
public class Section implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sectionId")
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "lineId")
#JsonManagedReference
private Line line;
...
}
#Entity
#Table(name = "line")
public class Line implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "lineId")
private Integer id;
#OneToMany(mappedBy = "line", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonBackReference
private Set<Section> sections = new HashSet<Section>();
...
}
I solved my issue by using .addEntity(Transportation.class) like this :
String query = "select DISTINCT * from moyen_transport transp"
+ " join prix_voyage p on p.ID_MoyenTransport = transp.ID_MoyenTransport"
+ " where p.ID_Troncon = ( select t.ID_Troncon from troncon t where t.ID_Troncon = ( select l.ID_Ligne from ligne l where l.ID_Ligne = 1000000000) )";
Query result = getSession().createSQLQuery(query).addEntity(Transportation.class)**;
List<Transportation> results = result.list();
I have two classes:
#Entity
public class Tick {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(optional = false)
#JoinColumn(name = "elitesystem_id", referencedColumnName = "id")
private EliteSystem eliteSystem;
private Date createDate;
#ManyToOne(optional = true)
#JoinColumn(name = "commander_id", referencedColumnName = "id")
private Commander commander;
private String address;
and
#Entity
public class Note {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(optional = true)
#JoinColumn(name = "tick_id", referencedColumnName = "id")
private Tick tick;
private String text;
private Date createDate;
I want to select all ticks and get notes if there are any:
Query query = session.createQuery("select t, n from Note n right join n.tick t where t.commander.name = '123'");
List<Object[]> list = query.list();
This returns only Tick objects. What is the correct approach to get the Note information as well in 1 single query?
I could put a reference to a Note into the Tick class, but this doesnt sound right, as there are only a few notes, so the column in the Tick table would mostly be empty.
Create a New class for example:
public class TickNote {
private Tick tick;
private Note note;
public TickNote(Tick tick,Note note){
this.tick=tick;
this.note=note;
Then your query is:
Query query = session.createQuery("select NEW TickNote(t, n) from Note n right join n.tick t where t.commander.name = '123'");
List<TickNote> list = query.list();
I'm trying to implement a Keyword search functionality that returns a List of Keyword entities based on a field text match.
Right now, the query
select * from photo_keywords pk
inner join keywords k on pk.photo_id = k.keyword_id
inner join photos p on pk.keyword_id = p.photo_id
where k.keyword LIKE "%$SOME_SEARCH_VALUE%";
returns all matching photos for a given keyword search. I'd like to have this adapted to a #NamedQuery with the following Entity objects:
#Entity
#Table(name = "keywords")
public class Keyword implements Serializable{
#Id
#Column(name = "keyword_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String keyword;
#ManyToMany(mappedBy = "keywords")
private List<Photo> photos;
//getters and setters
}
and
#Entity
#Table(name = "photos")
public class Photo implements Serializable{
#Id
#Column(name = "photo_id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "photo_name", nullable = false)
private String photoName;
#Column(name = "photo_path", nullable = false)
private String photoPath;
#Column(name = "upload_date", nullable = false)
private Date uploadDate;
#Column(name = "view_count", nullable = false)
private int viewCount;
#Column(name = "capture_date", nullable = false)
private Date captureDate;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "photo_metadata")
#MapKeyColumn(name = "metadata_name")
#Column(name = "metadata_value")
private Map<String, String> photoMetadata;
#ManyToMany
#JoinTable(name = "photo_keywords",
joinColumns = #JoinColumn(name = "keyword_id"),
inverseJoinColumns = #JoinColumn(name = "photo_id"))
public List<Keyword> keywords;
//getters and setters
}
This creates a join table photo_keywords, rather than a JoinColumn.
What I've tried so far with the Keyword entity:
#NamedQueries({
#NamedQuery(
name = "findKeywordByName",
query = "SELECT keyword from Keyword k WHERE k.keyword = :keyword"
)
})
which is executed via
public Keyword findKeywordByString(String keyword){
Keyword thisKeyword;
Query queryKeywordExistsByName = getEntityManager().createNamedQuery("findKeywordByName");
queryKeywordExistsByName.setParameter("keyword", keyword);
try {
thisKeyword = new Keyword((String) queryKeywordExistsByName.getSingleResult());
} catch (NoResultException e){
thisKeyword = null;
}
return thisKeyword;
}
This returns the Keyword, but with the photos property being null. This is to be expected, since I'm only selecting the keyword property. How can I adapt the SQL query above to a #NamedQuery?