QueryDSL delete method - spring-data

I'm using spring-data-mongodb 1.2.0 with QueryDSL 2.9.0.
Why doesn't the QueryDslPredicateExecutor have a delete(Predicate predicate) method?
Is there a workaround?

What you can probably do is this. With the predicate for "where" condition, query for the objects and then pass that to the delete method
QMyObj obj= new QMyObj("myObj");
Iterable<MyObj> myObjs = myObjRepository.findAll(obj.property.eq("property"));
myObjRepository.delete(myObjs);
Here I am first creating an instance of the Q class and then finding all the objects based on the predicate. Then calling the repository's void delete(Iterable<? extends T> entities) method.
May be it is because of this workaround they don't provide it, but that is for the Spring Source guys to confirm

I was able to perform this by:
#Autowired
LocalContainerEntityManagerFactoryBean emFactory;
EntityManager em;
#PostConstruct
private void setup(){
em = emFactory.getObject().createEntityManager();
}
#Transactional private voide deleteByPredicate(Predicate predicate) {
JPADeleteClause deleteClause = new JPADeleteClause(em, QPersonEntity.personEntity);
em.joinTransaction();
deleteClause.where(predicate).execute();
}

using JPADeleteClause, pass two following parameters to it:
entityManager and EntityPath QClass
QPersonEntity path = QPersonEntity.personEntity;
JPADeleteClause deleteClause = new JPADeleteClause(getEntityManager(), path);
deleteClause.where(path.name.eq("behrooz")).execute();

Related

Run method after deploying and save the result with to database

I have started a Quarkus project and i have there 2 tables, i am trying to run a Method immediately after deploying, In the method i use Entitymanger to save some results in the database.
In pure Jakarta EE, you could an EJB and annotate it with #Startup. But Since quarkus uses CDI.
#ApplicationScoped
public class StartApp {
private static final String PERSISTENCE_UNIT_NAME = "Employee";
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
EntityManagerFactory factory =
Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
Directory directory = new Directory("/some/info", true, false, ".xml");
em.persist(directory);
em.close();
}
}
How can i do that!? some guess !
I think what you need is this:
#ApplicationScoped
class StartApp {
void startup(#Observes StartupEvent event) {
// Do whatever needs to be done.
}
}
More information and options can be found on the very well documented quarkus pages. https://quarkus.io/guides/cdi-reference#startup-event
Ps. don't forget about your transactions and maybe take a look at Hibernate ORM.

Why Lazy Collections do not work with JavaFX getters / setters?

I experienced poor performance when using em.find(entity, primaryKey).
The reason seems to be that em.find() will also load entity collections, that are annotated with FetchType.LAZY.
This small test case illustrates what I mean:
public class OriginEntityTest4 {
[..]
#Test
public void test() throws Exception {
final OriginEntity oe = new OriginEntity("o");
final ReferencePeakEntity rpe = new ReferencePeakEntity();
oe.getReferencePeaks().add(rpe);
DatabaseAccess.onEntityManager(em -> {
em.persist(oe);
em.persist(rpe);
});
System.out.println(rpe.getEntityId());
DatabaseAccess.onEntityManager(em -> {
em.find(OriginEntity.class, oe.getEntityId());
});
}
}
#Access(AccessType.PROPERTY)
#Entity(name = "Origin")
public class OriginEntity extends NamedEntity {
[..]
private final ListProperty<ReferencePeakEntity> referencePeaks =
referencePeaks =
new SimpleListProperty<>(FXCollections.observableArrayList(ReferencePeakEntity.extractor()));
#Override
#OneToMany(mappedBy = "origin", fetch = FetchType.LAZY)
public final List<ReferencePeakEntity> getReferencePeaks() {
return this.referencePeaksProperty().get();
}
public final void setReferencePeaks(final List<ReferencePeakEntity> referencePeaks) {
this.referencePeaksProperty().setAll(referencePeaks);
}
}
I cannot find any documentation on that, my question is basically how can I prevent the EntityManager from loading the lazy collection?
Why I need em.find()?
I use the following method to decide whether I need to persist a new entity or update an existing one.
public static void mergeOrPersistWithinTransaction(final EntityManager em, final XIdentEntity entity) {
final XIdentEntity dbEntity = em.find(XIdentEntity.class, entity.getEntityId());
if (dbEntity == null) {
em.persist(entity);
} else {
em.merge(entity);
}
}
Note that OriginEntity is a JavaFX bean, where getter and setter delegate to a ListProperty.
Because FetchType.LAZY is only a hint. Depending on the implementation and how you configured your entity it will be able to do it or not.
Not an answer to titles question but maybe to your problem.
You can use also em.getReference(entityClass, primaryKey) in this case. It should be more efficient in your case since it just gets a reference to possibly existing entity.
See When to use EntityManager.find() vs EntityManager.getReference()
On the other hand i think your check is perhaps not needed. You could just persist or merge without check?
See JPA EntityManager: Why use persist() over merge()?

How do I create a separate entity manager for bulk operations in a JTA environment?

In JPA, when doing bulk operations such as this
update LogEntry e set e.customer = null where e.customer.id = :cid
It is recommended to use a separate entity manager to avoid breaking synchronization, according to this: UPDATE SET Queries in JPA/JPQL
For example, the EntityManager may not be aware that a cached entity object in its persistence context has been modified by an UPDATE query. Therefore, it is a good practice to use a separate EntityManager for UPDATE queries.
How do I create a separate entity manager in a JTA environment such as Wildfly using hibernate? Do I need to create a separate persistence unit for bulk operations?
EDIT: Given I dont need a separate PU for bulk operations, is this a sufficient way of solving it using a new transaction?
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
#Inject
private EntityManager em;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
...
}
Where LogEntry.updateCustomerToNull is the bulk query.
Answer: This does not work because the interceptor is not invoked when called from inside the same class.
EDIT2: Following the suggestions from Andrei, this should work:
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
public static class BulkUpdater {
#Inject
private EntityManager em;
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
}
#Inject
private EntityManager em;
#Inject
private BulkUpdater bulkUpdater;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
bulkUpdater.updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
...
}
Testing confirms that the interceptor gets called twice.
The recommendation is valid only if you also do other stuff with the EntityManager (when there is a risk of manipulating/reading the same entities as the BULK UPDATE). The easiest solution: make sure that this BULK UPDATE is executed in a separate service, within a new transaction. No need to create a separate PU (persistence unit) for bulk operations.

JPA using #PersistenceContext

How to get the Persistence Unit Name dynamically?
For example in below example,I am hard coding unitName to application_openjpa, but I want to give it dynamically as unitName=#{unitName} so if I have different project uses the entity jar, but can use its own persistence.xml file
#PersistenceContext(unitName="application_openjpa")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
Since an application normally have limited number of persistence units, how about create two setter methods and annotate it with different unitName attribute, eg:
#PersistenceContext(unitName="testEM")
public void setTestEntityManager(EntityManager testEM) {
this.testEM = testEM;
}
#PersistenceContext(unitName="prodEM")
public void setProdEntityManager(EntityManager prodEM) {
this.prodEM = prodEM;
}
Then you can choose between testEM and prodEM dynamically ?

How to get entityManager by using an entity class

How to reach the entity manager which managed the entity. I mean; suppose that i have an entity reference in the sessionBean, how can i get entityManager of this entity belonged one?
I had already tried (plz see getEntityManagerOfEntity() method) contains method of em; but it does not work.
Thx
bgrds
#Stateless(name = "MainManager", mappedName = "MainManager")
#TransactionManagement(TransactionManagementType.CONTAINER)
#Interceptors(value = { PerformanceMonitor.class, ProfileInterceptor.class })
public class MainManagerBean implements MainManager, MainManagerLocal
{
private Logger logger = Logger.getLogger(this.getClass());
#PersistenceContext(unitName = "DSApp")
private EntityManager manager;
#PersistenceContext(unitName = "DSIX")
private EntityManager integrationManager;
#Resource
SessionContext ctx;
public EntityManager getEntityManagerOfEntity(SuperEntity superEntity)
{
if (manager.contains(superEntity))
return manager;
else if (integrationManager.contains(superEntity))
return integrationManager;
return null;
}
public SuperEntity findByPrimaryKey(SuperEntity superEntity)
{
getEntityManagerOfEntity(superEntity).setFlushMode(FlushModeType.COMMIT);
return dao.findByPrimaryKey(getEntityManagerOfEntity(superEntity), superEntity);
You cannot backtrack the EntityManager from an entity using the JPA API, even when it is still managed.
What you can do, if you have references to different EMs in your bean and the entity is managed, is to check the right EM by calling em.contains(entity).
In most cases it is not really important to know, which EM has fetched an entity originally, since you can merge the entity into any persistence context and continue working with it.