Cannot use ContextTransactionalCallable with TransactionProvider - spring-boot

I have an error, which I can't solve.
I am using Spring and JOOQ.
Error occurs here:
#Transactional
public UUID create(List<User> users) {
UUID uuid = UUID.randomUUID();
dslContext.transaction(() -> {
dslContext
.insertInto(APPLE, APPLE.APPLE_ID, APPLE.TITLE)
.values(uuid, uuid.toString())
.execute();
users.forEach(user -> {
dslContext
.insertInto(APPLE_MEMBERS, APPLE_MEMBERS.APPLE_ID, APPLE_MEMBERS.USER_ID)
.values(uuid, user.getUserId())
.execute();
});
});
return uuid;
}
Error:
org.jooq.exception.ConfigurationException: Cannot use ContextTransactionalCallable with TransactionProvider of type class org.springframework.boot.autoconfigure.jooq.SpringTransactionProvider
Maybe someone had same error or has idea how to solve this error?

Using out of the box features:
You have to pick one of the two approaches:
Spring's declarative transaction management through annotations
jOOQ's programmatic transaction management through its API
Out of the box, they cannot be combined. In your particular case, I don't see why you would want to do it. The nested, programmatic transaction has the exact same scope as the outer, declarative transaction. It is redundant.
Using custom TransactionProvider implementations
You could write your own TransactionProvider that is able to communicate with Spring's transaction management and allows for embedding nested transactions in #Transactional annotated methods, but I generally advise against it. Pick one of the two approaches.

Related

SpringBoot build query dynamically

I'm using SpringBoot 2.3.1 and Spring Data for accessing to PostgreSQL. I have the following simple controller:
#RestController
public class OrgsApiImpl implements OrgsApi {
#Autowired
Orgs repository;
#Override
public ResponseEntity<List<OrgEntity>> listOrgs(#Valid Optional<Integer> pageLimit,
#Valid Optional<String> pageCursor, #Valid Optional<List<String>> domainId,
#Valid Optional<List<String>> userId) {
List<OrgEntity> orgs;
if (domainId.isPresent() && userId.isPresent()) {
orgs = repository.findAllByDomainIdInAndUserIdIn(domainId.get(), userId.get());
} else if (domainId.isPresent) {
orgs = repository.findAllByDomainIdIn(domainId.get());
} else if (userId.isPresent()) {
orgs = repository.findAllByUserIdIn(userId.get());
} else {
orgs = findAll();
}
return ResponseEntity.ok(orgs);
}
}
And a simple JPA repository:
public interface Orgs extends JpaRepository<OrgEntity, String> {
List<OrgEntity> findAllByDomainIdIn(List<String> domainIds);
List<OrgEntity> findAllByUserIdIn(List<String> userIds);
List<OrgEntity> findAllByDomainIdInAndUserIdIn(List<String> domainIds, List<String> userIds);
}
The code above has several obvious issues:
If number of query parameters will grow, then this if is growing very fast and too hard to maintain it. Question: Is there any way to build query with dynamic number of parameters?
This code doesn't contain a mechanism to support cursor. Question: Is there any tool in Spring Data to support query based on cursor?
The second question can be easily get read if first question is answered.
Thank you in advance!
tl;dr
It's all in the reference documentation.
Details
Spring Data modules pretty broadly support Querydsl to build dynamic queries as documented in the reference documentation. For Spring Data JPA in particular, there's also support for Specifications on top of the JPA Criteria API. For simple permutations, query by example might be an option, too.
As for the second question, Spring Data repositories support streaming over results. That said, assuming you'd like to do this for performance reasons, JPA might not be the best fit in the first place, as it'll still keep processed items around due to its entity lifecycle model. If it's just about access subsets of the results page by page or slice by slice, that's supported, too.
For even more efficient streaming over large data sets, it's advisable to resort to plain SQL either via jOOQ (which can be used with any Spring Data module supporting relational databases), Spring Data JDBC or even Spring Data R2DBC if reactive programming is an option.
You can use spring-dynamic-jpa library to write a query template
The query template will be built into different query strings before execution depending on your parameters when you invoke the method.

Does it make sense to use Spring's #Transactional annotation in a method which doesn't talk to database?

I am trying to understand a project which involves spring's declarative transactional annotation. The body of the function is not making any JDBC calls or calling JPA repository methods. All it is doing is call some third party API and transform it's results, it's not even saving the response in the database. So no database interactions at all.
My question is that is it worth using #Transactional annotation on this method? Because as per my understanding #Transactional only helps in maintaining consistency in databases.
The #Transactional annotation defines the scope of a single database transaction - all it does is it begins the transaction and either commit it or rollback. It allows to manage transaction declarative way rather than do it programatically every time.
It looks something like this:
UserTransaction utx = entityManager.getTransaction();
try {
utx.begin();
// your method invocation here
utx.commit();
} catch(RuntimeException ex) {
utx.rollback();
throw ex;
}
By making method #Transactional when there is no database calls you only make its performance worse as redundant steps have to take place when not necessary.

QueryException when using Spring Data Rest with EclipseLink on Multi-Tenant System

I am using Spring data rest and EclipseLink to create a multi-tenant single table application.
But I am not able to create an Repository where I can call on custom QueryParameters.
My Kid.class
#Entity
#Table(name="kid")
#Multitenant
public class Kid {
#Id
private Long id;
#Column(name = "tenant_id")
private String tenant_id;
#Column(name = "mother_id")
private Long motherId;
//more attributes, constructor, getter and setter
}
My KidRepository
#RepositoryRestResource
public interface KidRepository extends PagingAndSortingRepository<Kid, Long>, QuerydslPredicateExecutor<Kid> {}
When I call localhost/kids I get the following exception:
Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a):
org.eclipse.persistence.exceptions.QueryException\r\nException Description: No value was provided for the session property [eclipselink.tenant-id].
This exception is possible when using additional criteria or tenant discriminator columns without specifying the associated contextual property.
These properties must be set through EntityManager, EntityManagerFactory or persistence unit properties.
If using native EclipseLink, these properties should be set directly on the session.
When I remove the #Multitenant annotation on my entity, everything works fine. So it has definitively something to do with EclipseLink.
When I don't extend from the QuerydslPredicateExecutor it works too. But then I have to implement all findBy* by myself. And even doing so, it breaks again. Changing my KidsRepository to:
#RepositoryRestResource
public interface KidRepository extends PagingAndSortingRepository<Kid, Long> {
Collection<Kid> findByMotherId(#Param("motherId") Long motherId);
}
When I now call localhost/kids/search/findByMotherId?motherId=1 I get the same exception as above.
I used this tutorial to set up EcpliseLink with JPA: https://blog.marcnuri.com/spring-data-jpa-eclipselink-configuring-spring-boot-to-use-eclipselink-as-the-jpa-provider/, meaning the PlatformTransactionManager, the createJpaVendorAdapter and the getVendorProperties are overwritten.
The tenant-id comes with a jwt and everything works fine as long as I don't use QuerydslPredicateExecutor, which is mandatory for the use case.
Turns out, that the wrong JpaTransactionManager is used we I rely on the QuerydslPredicateExecutor. I couldn't find out, which one is created, but having multiple breakpoints inside the EclipseLink Framework code, non of them were hit. This is true for both, using the QuerydslPredicateExecutor or using the custom findby method.
I have googled a lot and tried to override some of the basic EclipseLink methods, but non of that worked. I am running out of options.
Does anyone has any idea how to fix or work around this?
I was looking for a solution for the same issue; what finally helped was adding the Spring's #Transactional annotation to either Repository or any place from where this custom query is called. (It even works with javax.transactional.) We had the #Transactional annotation on most of our services so the issue was not obvious and its occurrence seemed rather accidental.
More detailed explanation about using #Transactional on Repository is here: How to use #Transactional with Spring Data?.

Best practices for Spring Transactions and generic DAOs & Services

I work on a Java EE application with Spring and JPA (EclispeLink). We developed a user-friendly interface for administrating the database tables. As I know more about Spring and Transactions now, I decided to refactor my code to add better transaction management. The question is how to best deal with generic DAOs, generic services and Spring transactions?
Our current solution was:
A generic BasicDAO which deals with all common database actions (find, create, update, delete...)
A DaoFactory which contains a map of implementations of BasicDao for all entity types (which only need basic database actions) and which gets the entitymanager injected by spring to pass it to the daos
A generic BasicService which offers the common services (actually directly linked to the dao methods)
A ServiceFactory which contains a map of implementations of BasicService for all entity types, which gets the daoFactory injected and passes it to the services. It has a method "getService(Class T)" to provide the right service to the controllers.
Controllers corresponding to the right entity types which delegate their requests to a generic controller which handles the request parameters using reflection and retrieves the right service from the service factory's map to call the update/create/remove method.
Problem is that, when I add the #Transactionnal annotations on the generic Service and my serviceFactory creates the typed services in its map, these services don't seem to have active transactions running.
1) Is it normal, due to the genericity and the fact that only spring-managed services can have transactions?
2) What is the best solution to solve my problem:
Create managed typed services only implementing the generic service and inject them directly in my serviceFactory?
Remove the service layer for these basic services? (but maybe I'll get the same problem with transactions on my dao generic layer...)
Other suggestions?
I read some questions related to these points on the web but couldn't find examples which went as far into genericity as here, so I hope somebody can advise me... Thanks in advance!
For basic gets you don't need a service layer.
A service layer is for dealing with multiple aggregate roots - ie complex logic invloving multiple different entities.
My implementation of a generic repository looks like this :
public class DomainRepository<T> {
#Resource(name = "sessionFactory")
protected SessionFactory sessionFactory;
public DomainRepository(Class genericType) {
this.genericType = genericType;
}
#Transactional(readOnly = true)
public T get(final long id) {
return (T) sessionFactory.getCurrentSession().get(genericType, id);
}
#Transactional(readOnly = true)
public <T> List<T> getFieldEquals(String fieldName, Object value) {
final Session session = sessionFactory.getCurrentSession();
final Criteria crit = session.createCriteria(genericType).
add(Restrictions.eq(fieldName, value));
return crit.list();
}
//and so on ..
with different types instantiated by spring :
<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
<constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>
and can be referenced like so :
#Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;
And can also be extended manually for complex entities.

JPA and DAO - what's the standard approach?

I'm developing my first app with JPA/Hibernate and Spring. My first attempt at a DAO class looks like this:
#Repository(value = "userDao")
public class UserDaoJpa implements UserDao {
#PersistenceContext
private EntityManager em;
public User getUser(Long id) {
return em.find(User.class, id);
}
public List getUsers() {
Query query = em.createQuery("select e from User e");
return query.getResultList();
}
}
I also found some examples using JpaDaoSupport and JpaTemplate. Which design do you prefer? Is there anything wrong with my example?
I'd say your approach looks totally sound. Personally I don't use JpaDaoSupport or JpaTemplate because you can do everything you need with the EntityManager and Criteria Queries.
Quote from the JavaDoc of JpaTemplate:
JpaTemplate mainly exists as a sibling of JdoTemplate and HibernateTemplate, offering the same style for people used to it. For newly started projects, consider adopting the standard JPA style of coding data access objects instead, based on a "shared EntityManager" reference injected via a Spring bean definition or the JPA PersistenceContext annotation.
I prefer the template-less approach (i.e. your current approach) because
it's less invasive, you don't tie DAOs to Spring
templates don't offer much value with APIs that use unchecked exceptions
And this is the Spring recommendation, as summarized in the blog post "So should you still use Spring's HibernateTemplate and/or JpaTemplate??" and the official javadoc:
The real question is: which approach to choose??
(...)
So in short (as the JavaDoc for
HibernateTemplate and
JpaTemplate already mention)
I'd recommend you to start using the
Session and/or EntityManager API
directly if you're starting to use
Hibernate or JPA respectively on a new
project–remember: Spring tries to be
non-invasive, this is another great
example!
I, personally, prefer your approach - inject EntityManager and use it directly. But JpaTemplate is also a good option. I don't like it, because adds yet another, unnecessary layer of abstraction.
I don't know if there's a "standard" approach.
If you're using JPA, you have your choice of implementations: Hibernate, TopLink, etc.
If you deploy to Google App Engine, you'll use JPA talking to BigTable.
So if your objectives are to maximize portability, stick with the JPA standard, and not tie yourself to a particular implementation like Hibernate, make sure that your DAOs only use JPA constructs.

Resources