I have to delete all the records for matching ID except one. There is no dat column which I can consider. The dtatastruchtur e is like below:
OBJECT_ID - primary key
DOC_ID
FIRST NAME
LAST NAME
I am trying to delete all the doc_ID that match to specific number except one through jpa. Couldn't find any jpa specific function. I am implementing JPARepository. There is delete All, Is there any way I can achieve this?
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
public User save(User user);
public void deleteAllByDocID(String docID);
}
UPDATE: Sorry was unsure of the database: we are using DB2 and I tried to write down these queries:
DELETE FROM USR WHERE OBJ_ID NOT IN (SELECT OBJ_ID FROM USR WHERE DOC_OBJ_ID='91298' FETCH FIRST 1 ROWS ONLY); - this gives me error transaction log is full.
ANd this:
DELETE FROM USR WHERE OBJ_ID NOT IN (SELECT TOP(1) OBJ_ID FROM USR WHERE DOC_OBJ_ID='91298'); - and this give me error TOP is not a function
if you don't want to use a custom query, you can do it like this :
in your repository define these two methods
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findFirstByDocIDOrderByObjectIDAsc(String docID);
void deleteAllByDocIDAndObjectIDNot(String docID, Long objectID);
}
and then in your service layer do this
User user = repository.findFirstByDocIDOrderByObjectIDAsc(docID);
if(user !=null) deleteAllByDocIDAndObjectIDNot(docID, user.objectID);
note that since this solution is using derived delete query, it will delete the records one by one.
You could COUNT matching records first and then delete with LIMIT N-1
Related
Is there any simpler way to delete the records of table with are mapped by #OneToMany and #ManyToOne JPA mapping. Currently I am using PostgreSQL as my database. There are 1000 records ranging from 0 to 999 which I want to delete, and rest I want to keep. As deleting every record with deletion of there references(foreign key) will be very hectic. Just want a simpler way to do this.
You can always execute a DELETE query in JPA:
int rowsDeleted = entityManager
.createQuery("DELETE FROM MyEntity WHERE id >= 0 AND id <=999")
.executeUpdate();
If there are foreign keys, you need some extra work:
int childRowsDeleted = entityManager
.createQuery("DELETE FROM MyChildEntity WHERE parent.id >= 0 AND parent.id <=999")
.executeUpdate();
int rowsDeleted = entityManager
.createQuery("DELETE FROM MyEntity WHERE id >= 0 AND id <=999")
.executeUpdate();
If there are many foreign keys, perhaps you can consider JPA cascade remove:
#OneToOne(cascade={CascadeType.REMOVE})
and then entityManager.remove() each entity but this will have performance implications since you will end up with many DELETE queries, each deleting a single row.
Got this with introducing #DeleteMapping, and it worked without even using
" #OnDelete(action = OnDeleteAction.CASCADE)" this annotation above the mapped variable declaration in the child class
[https://www.callicoder.com/hibernate-spring-boot-jpa-one-to-many-mapping-example/#get-paginated-posts-get-postspage0size2sortcreatedatdesc], We can follow this link to get a complete overview.
#DeleteMapping("/posts/{postId}")
public ResponseEntity<?> deletePost(#PathVariable Long postId) {
return postRepository.findById(postId).map(post -> {
postRepository.delete(post);
return ResponseEntity.ok().build();
}).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
}
I have three tables. I want to display all data from cms_planner table together with Topic Name from cms_topic table. To achieve that, I need to go through the cms_subject table.
I want to use belongsToMany but I already have cms_subject table that holds the foreign key for cms_planner and the foreign key for cms_topic. The table name does not represent pivot key.
I also want to use hasManyThrough but it doesn't work. I'm thinking to inverse the hasManyThrough.
How can I achieve that?
1. CmsPlanner
i. planner_id
ii. subject_id
iii. date_start
2. CmsSubject
i. subject_id
ii. topic_id
3. CmsTopic
i. topic_id
ii. topic name
In CmsPlanner model
public function subject(){
return $this->hasManyThrough(
'App\CmsTopic',
'App\CmsSubject',
'topic_id', 'topic_id', 'planner_id');}
In CmsPlanner controller
CmsPlanner::with('subject')->get();
Add this relation on CmsSubject
public function cmsTopic()
{
return $this->belongsTo('App\Models\CmsTopic', 'topic_id', 'topic_id');
}
then add following relation on CmsPlanner
public function cmsSubject()
{
return $this->belongsTo('App\Models\CmsSubject', 'subject_id', 'subject_id');
}
to get data
$cms_planner = CmsPlanner::with('CmsSubject')->where('id', $planner_id)->get();
'user' => $this->business->user
I have a User table which looks like below
UserID Name SponsorID (FK)
1 A null
2 B 1
3 C 1
4 D 3
The SponsorID refers to UserID. Now I need write a query which returns all user who is descendant of a given UserID.
Example
For UserID 1 the query returns all 4 users
For UserID 3 the query should return 1 user
The current implementation is getting the user list by looping each direct downline and I am looking for a better solution if it's possible.
UPDATE
Current code
public void findDownlineSponsorByUserBO(UserBO rootBO) throws Exception {
List<UserBO> downlines = businessOperationService.findUserBySponsorId(rootBO.getId(), "createdDate", false);
memberList.addAll(downlines);
for (UserBO memberBO : downlines) {
findDownlineSponsorByUserBO(memberBO);
}
}
You're going to have to use an iterative or recursive solution here, unless (perhaps) you're limited to one level of sponsor and can relate UserID to SponsorID in one join. You could load the table into a tree structure in memory, and then query that. Loading it would be O(nlogn), but then traversing it would be O(logn).
This other SO question might give you some useful ideas: Is it possible to query a tree structure table in MySQL in a single query, to any depth?
SQL 2008 | .NET 4.0 | NHibernate 3.1 | NHibernate.Castle 3.1 | Castle.Core 2.5.2
So I have a linking table with metadata, like the author of this question NHibernate Mapping a Many to Many with Data on Join Table
Initially, I mapped just like the answer to this question as it seemed the most parsimonious way to handle it. However, after turning on show_sql and observing what was going on, the ID lookups ended up yielding N+1 queries where N is the number of associations.
Observe this example database that is analogous to my actual data, defined in sql-like syntax
CREATE TABLE [User]
(
Id int PRIMARY KEY
)
CREATE TABLE UserPref
(
Id int PRIMARY KEY,
Name varchar(32)
)
CREATE TABLE UserPrefAssociation
(
UserId int,
PrefId int,
Value varchar(32)
)
I hacked the following code together with this User one-to-many object mapping IList<UserPrefAssociation> Preferences { get; set; }
public IDictionary<string, string> GeneratePrefDict()
{
return Preferences
.ToDictionary(i => i.UserPref.Name, i => i.Value);
}
Sure, this works great, but as mentioned before, each i.UserPref.Name, is an additional query to SQL.
After playing in SQL, I have found the query that accomplishes what I want. My question then becomes how can I do this with NHibernate?
SELECT UserPref.Name, UserPrefAssociation.Value
FROM [User]
INNER JOIN UserPrefAssociation ON [User].Id = UserPrefAssociation.UserId
INNER JOIN UserPref ON UserPrefAssociation.UserPrefId = UserPref.Id
WHERE [User].Id = 1
~~~~SOLVED~~~~~
using NHibernate.Linq;
...
public IDictionary<string, string> GeneratePrefDict(ISession s)
{
return
(from entry in s.Query<User_UserPref>()
where entry.User == this
select new
{
key = entry.UserPref.Name,
value = entry.Value
})
.ToDictionary(i => i.key, i => i.value);
}
Generates this SQL
NHibernate: select userpref1_.Name as col_0_0_, user_userp0_.Value as col_1_0_ f
rom User_UserPref user_userp0_ left outer join UserPref userpref1_ on user_userp
0_.UserPrefId=userpref1_.Id where user_userp0_.UserId=#p0;#p0 = 1 [Type: Int32 (
0)]
Which is better than N+1 queries, and solves my issue.
I think you can achieve what you are wanting with Futures and QueryOver. Take a look at the following article:
Fighting cartesian product (x-join) when using NHibernate 3.0.0
If you can't visualize how to accomplish what you need from the above I can tailor that example more to your needs.
I have an entity called Books that can have a list of more books called RelatedBooks.
The abbreviated Book entity looks something likes this:
public class Book
{
public virtual long Id { get; private set; }
public virtual IList<Book> RelatedBooks { get; set; }
}
Here is what the mapping looks like for this relationship
HasManyToMany(x => x.RelatedBooks)
.ParentKeyColumn("BookId")
.ChildKeyColumn("RelatedBookId")
.Table("RelatedBooks")
.Cascade.SaveUpdate();
Here is a sample of the data that is then generated in the RelatedBooks table:
BookId RelatedBookId
1 2
1 3
The problem happens when I Try to delete a book. If I delete the book that has an ID of 1, everything works ok and the RelatedBooks table has the two corresponding records removed. However if I try to delete the book with an ID of 3, I get the error "The DELETE statement conflicted with the REFERENCE constraint "FK5B54405174BAB605". The conflict occurred in database "Test", table "dbo.RelatedBooks", column 'RelatedBookId'".
Basically what is happening is the Book cannot be deleted because the record in the RelatedBooks table that has a RelatedBookId of 3 is never deleted.
How do I get that record to be deleted when I delete a book?
EDIT
After changing the Cascade from SaveUpdate() to All(), the same problem still exists if I try to delete the Book with an ID of 3. Also with Cascade set to All(), if delete the Book with and ID of 1, then all 3 books (ID's: 1, 2 and 3) are deleted so that won't work either.
Looking at the SQL that is executed when the Book.Delete() method is called when I delete the Book with an ID of 3, it looks like the SELECT statement is looking at the wrong column (which I assume means that the SQL DELETE statment would make the same mistake, therefore never removing that record). Here is the SQL for the RelatedBook
SELECT relatedboo0_.BookId as BookId3_
, relatedboo0_.RelatedBookId as RelatedB2_3_
, book1_.Id as Id14_0_
FROM RelatedBooks relatedboo0_
left outer join [Book] book1_ on relatedboo0_.RelatedBookId=book1_.Id
WHERE relatedboo0_.BookId=3
The WHERE statment should look something like this for thie particular case:
WHERE relatedboo0_.RelatedBookId = 3
SOLUTION
Here is what I had to do to get it working for all cases
Mapping:
HasManyToMany(x => x.RelatedBooks)
.ParentKeyColumn("BookId")
.ChildKeyColumn("RelatedBookId")
.Table("RelatedBooks");
Code:
var book = currentSession.Get<Book>(bookId);
if (book != null)
{
//Remove all of the Related Books
book.RelatedBooks.Clear();
//Get all other books that have this book as a related book
var booksWithRelated = currentSession.CreateCriteria<Book>()
.CreateAlias("RelatedBooks", "br")
.Add(Restrictions.Eq("br.Id", book.Id))
.List<Book>();
//Remove this book as a Related Book for all other Books
foreach (var tempBook in booksWithRelated)
{
tempBook.RelatedBooks.Remove(book);
tempBook.Save();
}
//Delete the book
book.Delete();
}
Rather than setting the cascade attribute, I think you need to simply empty the RelatedBooks collection before deleting a book.
book.RelatedBooks.Clear();
session.Delete(book);
Cascading deletes is not typically done in a many-to-many relationship because it will delete the object at the other end of the relationship, in this case a Book.
This just got updated:
http://fluentnhibernate.lighthouseapp.com/projects/33236/tickets/115-self-referencing-relationships