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"));
}
Related
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
I have this mapping:
#Entity
#Table(name="CUSTOMER")
class Customer {
#Id
#Column(name="CUSTOMER_ID")
Long id;
...
#ElementCollection
#CollectionTable(name="CUSTOMER_ADDRESS", joinColumns=#JoinColumn(name="CUSTOMER_ID"))
List<CustomerAddress> addresses;
}
#Embeddable
class CustomerAddress {
String street;
String zip;
String ...
String ...
...
}
I have two saved addresses for customer ID : 1. If i update this customer, having a single address on, Eclipselink tries deletes the missing address row using all CustomerAddress fields on DELETE where statement:
DELETE FROM CUSTOMER_ADDRESS WHERE STREET = ?, ..., ZIP = ?, ..., CUSTOMER_ID = ?
The issue is i may have accents and other data that prevents the WHERE statement to match the row using all fields. Is there anyway to force Eclipselink to delete all user addresses and insert again?
DELETE FROM CUSTOMER_ADDRES WHERE CUSTOMER_ID = ?
INSERT ALL FROM COLLECTION
I could not find a way to accomplish what i wanted. It seems Hibernate has the behavior i expect.
Changed ElementCollection to OneToMany and changed the model to have an ID under CustomerAddress.
Using ObjectContext. I'm wanting to do this by passing an SQL query via the ExecuteStoreCommand since I don't fancy retrieving all relevant entities just for the sake of deleting them after.
The Category table is as so:
CatID | CatName | ParentID
Where CatID is the primary key to the ParentID FK
I am wishing to delete a category and also all those that
are under it. Can be 2+ levels deep of sub cats, so different ParentID's
Thought I could do it as below and just set "cascade" on delete option
for the foreign key in the database, but it won't let me and it does not appear to want to
cascade delete down by using the CatID - ParentID relationship and the query gets
stopped by this very FK constraint.
public RedirectToRouteResult DelCat(int CatID)
{
if (CatID != 0)
{
_db.ExecuteStoreCommand("DELETE FROM Categories WHERE CatID={0}", CatID);
_db.SaveChanges();
}
return RedirectToAction("CatManage");
}
Recursive CTE allCategories produces list of all categories in hierarchy. Delete part, obviously, deletes them all.
; with allCategories as (
select CatID
from Categories
where CatID = #CatID_to_delete
union all
select Categories.CatID
from allCategories
inner join Categories
on allCategories.CatID = Categories.ParentID
)
delete Categories
from Categories
inner join allCategories
on Categories.CatID = allCategories.CatID
Try it with select * from allCategories, though, to check first.
There is TEST # Sql Fiddle.
Why not just send two statements in your batch?
DELETE Categories WHERE ParentID = {0};
DELETE Categories WHERE CatID = {0};
If the framework you're using "won't let you" do that, then do this right: put your logic in a stored procedure, and call the stored procedure.
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