Hibernate : add child to parent collection - mysql

I'm using Hibernate on a web project, which a have two different classes (Node, Interface)
Node
#Entity
#Table(name="NODES")
public class Node {
//...
#OneToMany(mappedBy="node", cascade={CascadeType.ALL, CascadeType.MERGE, CascadeType.PERSIST}, orphanRemoval=true)
private Set<Interface> interfaces = new HashSet<>();
//...
}
Interface
#Entity
#Table(name="INTERFACES")
public class Interface {
//...
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="REF_NODE")
private Node node;
//...
}
Everything is fine,
My question is how can I add an Interface(chlid) to a Node(Parent) that already persisted? means if i already has some Node in the database with 2 Interfaces for exemple and I want to add the third one, how i can do it?
My first quick solution is to use native sql in Hibernate, like this:
public void addInteface(Interface i, Long idNode) {
//OpenSession..
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try {
//Start transaction
tx = session.beginTransaction();
//Native SQL in Hibernate
SQLQuery query = session.createSQLQuery("INSERT INTO INTERFACES (ID_INTERFACE, ALIAS, ID_NODE) VALUES (NULL, :Alias, :idNode)");
query.setParameter("ifAlias", i.getAlias());
query.setParameter("refNode", idNode);
//Some other parameters...
//Execute and Commit
query.executeUpdate();
tx.commit();
} catch (Exception e) {
if (tx != null) tx.rollback();
throw e;
} finally {
session.close();
}
}
It works, but it's not the best solution i believe.
NB: I found some topics here in stackoverflow with the almost same title but didn't answer my question.

It can be done in 2 ways.
1st Option:
node.getInterfaces().add(new Interface(....)); // you have orphan removal to true, this will work
session.saveOrUpdate(node);
tx.commit();
session.close();
Or
2nd OPtion
newInterface.setNode(nodeObject);
session.saveOrUpdate(newInterface);
tx.commit();
session.close();
In the above 2 methods, 2nd option works great compared to the first one. 1st one will pull all the children when you call getInterfaces(), which does not perform better.

//Syntax may not be on point, also some psuedcode
Lets say you want to add an interface to Node with an id that is 1:
Query query = session.createQuery("Select n from Node n where n.id = '1'");//JPQL
Node n = query.getSingleResult();
Then you will make a new Interface
Interface i = new Interface();
Then set the variables to what you want but also set the node
i.setVariables(..);
i.setNode(n);
Then update
session.merge(i);
Now all this is assuming generator id is correct and such.

Related

bulk update springboot jpa

I need to update single column(status) of more than 1000-2000 transactions at once.The value to be updated is same e.g "PROCESSED" for all the rows. I don't want to iterate on my list and update but looking for a bulk update in jpa itself. I also want to avoid explicit use of entity manager.
Something like this will work, with standard Spring-boot JPA:
public interface WidgetRepository extends CrudRepository<Widget, Long> {
#Override
List<Widget> findAll();
#Modifying(clearAutomatically = true, flushAutomatically = true)
#Query("update Widget w set w.status = :status where w in :widgets")
void setStatus(#Param("status") String status, #Param("widgets") List widgets);
}
Test code:
List<Widget> updates = new ArrayList<>();
updates.add(all.get(0));
updates.add(all.get(2));
repository.setStatus("status B", updates);
It should do all the updates in a single transaction.

PESSIMESTIC LOCK is not working with Spring Data accessing MySQL

I am using Spring Boot to build a scheduled-job data processing application. The main logic would be in a scheduled job that takes a batch of records and process them. I should be running 2 instances of the application that should not pick the same record twice. I tried to utilize the PESSIMISTIC LOCK with NO WAIT to resolve any records selection conflict.
Things are not working as expected. Both instances are picking the same records, although I was expecting only one instance to lock and process a few records and the other instance skip what was locked by the first instance.
Spring Boot version: 2.2.4.RELEASE
Database: MySQl
First I tried using the #Lock and #QueryHint annotations:
#Lock(value = LockModeType.PESSIMISTIC_WRITE) // adds 'FOR UPDATE' statement
#QueryHints(value={#QueryHint(name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED+"")})
Page<Transaction> findByStatus(String status, Pageable pageable);
Even with WAIT_FOREVER, there is no change in behavior as if #QueryHints are totally ignored..
The other option I tried is using NativeQuery:
#Query(value ="select * from transaction t where t.status = ?1 limit ?2 for update SKIP LOCKED",
countQuery="select count(*) from transaction t where t.status = ?1",
nativeQuery = true)
List<Transaction> findByStatusNQ(String status, Integer pageSize);
Same behavior. No locking, both app instances are selecting the same set of data
This is the defined entity:
#Entity
public class Transaction {
#Id
private Long id;
private String description;
private String status;
private String managedBy;
#Temporal(TemporalType.TIMESTAMP)
private Date manageDate;
...
}
The caller service component is annotated with #Transactional to enforce creating new transaction for each execution:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public List<Transaction> updateTrxStatus(String oldStatus,String newStatus){
List<Transaction> trxs = this.executeUsingNQ(oldStatus);
if(trxs.size()>0) {
logger.info( "Start updating Data");
trxs.forEach(transaction -> {
transaction.setStatus(newStatus);
transaction.setManagedBy(instanceName);
transaction.setManageDate(new Date(System.currentTimeMillis()));
});
}else{
logger.info(" Nothing to process");
}
return trxs;
}
#Transactional(propagation = Propagation.REQUIRED)
public List<Transaction> executeUsingNQ(String oldStatus){
List<Transaction> trxs = trxRepo.findByStatusNQ(oldStatus,2);
return trxs;
}
#Transactional(propagation = Propagation.REQUIRED)
public List<Transaction> executeWithPage(String oldStatus){
Pageable firstPageWithTwoElements = PageRequest.of(0, 2);
Page<Transaction> trxs = trxRepo.findByStatus(oldStatus, firstPageWithTwoElements);
return trxs.getContent();
}
Hopefully someone can help identifying whether there is some coding issue or missing coniguration!!!!
It runs that the issue was caused by using an incorrect Dialect with MySql. That version of Dialect "MySQLDialect" assumes "MyISAMStorageEngine" as a default storage engine while creating tables. That engine does not support any type of transactions.
The only storage engine that supports transactions is "InnoDB" which is being selected as the default choice when using other Dialects like "MySQL55Dialect", "MySQL57Dialect" or "MySQL8Dialect"

JPA MySQL exponentially slower

I have a problem when switching pages on my webapp.
When I switch between pages (Making requests against my DB), the response time becomes exponentially slower. See for example output from chrome, where I switch between two pages repeatedly:
Chrome screendump
But it is session specific. If I open another window/browser, the problem isn't there (though still in the other session), until I start switiching pages again.
When the page loads, this is called:
ActualTestGrabber actualTestGrabber = new ActualTestGrabber();
concretetest = actualTestGrabber.getById(80);
And for the "grabber":
public Concretetest getById(int tf){
EntityManager manager = getManager();
TypedQuery<Concretetest> query = manager.createQuery("SELECT w FROM Concretetest w WHERE w.id=" +tf ,Concretetest.class);
Concretetest result = query.getSingleResult();
Close();
return result;
}
And the getManager and close:
#Transactional
public EntityManager getManager(){
emfactory = Persistence.createEntityManagerFactory( "DSLTestV4" );
entitymanager = emfactory.
createEntityManager( );
entitymanager.getTransaction( ).begin( );
return entitymanager;
}
#Transactional
public void Close(){
entitymanager.getTransaction().commit();
entitymanager.close();
emfactory.close();
}
Am I doing something wrong? Why does the querytime get exponentially worse?
Really hope someone can help.
EDIT:
SQL Code for Concretetest
Best regards
Benjamin

LINQ To SQL does not work when adding new object

I use the following code to insert a new record to my Users table:
public bool CreateUser(User obj)
{
obj.Id = Guid.NewGuid();
using (_db = new CMSDataContext())
{
obj.SiteId = SiteID;
_db.Users.InsertOnSubmit(obj);
_db.SubmitChanges();
}
return true;
}
I do not get any errors, and everything seems fine. I can read a record from database with same DataContext. But after the above method runs completely, I see nothing new in my Users table. Why?
Is the id column truly a PK in the sql server database?

Can I update 1 object only with Linq to SQL?

Its a simple question, but I'm not aware of the answer and I couldn't get it to work.
Can I update only one entity on the entire DataContext? Or should I follow plain ADO.NET for this operation only?
Edit:
public MyObject GetMyObjectById(int selectedId)
{
DataContext db = _dbManager.GetContext();
return db.MyObject.SingleOrDefault(p => p.Id == selectedId);
}
I am getting an object with the above query...
I am querying then for an integer...on another table/object
public int GetMyInteger()
{
DataContext db = _dbManager.GetContext();
return db.MyAnotherObject.FirstOrDefault().MyInteger;
}
Everything is fine for all my operations...but now i just want to update only the integer i got from the database...
public void SetMyInteger(int updInteger)
{
DataContext db = new DataContext(ConnectionString);
MyAnotherObject theEntity = db.MyAnotherObject.FirstOrDefault();
atheEntity.MyInteger = updInteger;
db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
The above method deleted MyObject i got from the first query!!! Of course if i use the static context DataContext tries to update MyObject and MyAnotherObject which seems the correct behaviour.
Edit:
I have changed the method getting the integer with a new datacontext as well and seems to working correctly, i have a strange thought on why called the delete method, because it was the method that was called, but again .. is working now...
Thank you all for your time.
Yes it's possible. What have you tried? It should be as simple as this:
using (var dc = new YourDataContext())
{
Person p = dc.Persons.Take(1).Single();
p.FirstName = "Ahmad";
dc.SubmitChanges();
}
Yes, you can:
Foo foo = dc.Foos.Where(foo => foo.Id == 345).Single();
foo.Name = "foo";
dc.SubmitChanges();