I am very new to Springboot and Spring Data JPA and working on a use case where I am required to create users in different databases.
The application will receive 2 inputs from a queue - username and database name.
Using this I have to provision the given user in the given database.
I am unable to understand the project architecture.
Since the query I need to run will be of the format - create user ABC identified by password;
How should the project look like in terms of model class, repositories etc? Since I do not have an actual table against which the query will be run, do I need a model class since there will be no column mappings happening as such.
TLDR - Help in architecturing Springboot-Spring Data JPA application configured with multiple data sources to run queries of the format : create user identified by password
I have been using this GitHub repo for reference - https://github.com/jahe/spring-boot-multiple-datasources/blob/master/src/main/java/com/foobar
I'll be making some assumptions here:
your database of choice is Oracle, based on provided syntax: create user ABC identified by password
you want to create and list users
your databases are well-known and defined in JNDI
I can't just provide code unfortunately as setting it up would take me some work, but I can give you the gist of it.
Method 1: using JPA
first, create a User entity and a corresponding UserRepository. Bind the entity to the all_users table. The primary key will probably be either the USERNAME or the USER_ID column... but it doesn't really matter as you won't be doing any insert into that table.
to create and a user, add a dedicated method to your own UserRepository specifying the user creation query within a #NativeQuery annotation. It should work out-of-the-box.
to list users you shouldn't need to do anything, as your entity at this point is already bound to the correct table. Just call the appropriate (and already existing) method in your repository.
The above in theory covers the creation and listing of users in a given database using JPA.
If you have a limited number of databases (and therefore a limited number of well-known JNDI datasources) at this point you can proceed as shown in the GitHub example you referenced, by providing different #Configuration classes for each different DataSource, each with the related (identical) repository living in a separate package.
You will of course have to add some logic that will allow you to appropriately select the JpaRepository to use for the operations.
This will lead to some code duplication and works well only if the needs remain very simple over time. That is: it works if all your "microservice" will ever have to do is this create/list (and maybe delete) of users and the number of datasources remains small over time, as each new datasource will require you to add new classes, recompile and redeploy the microservice.
Alternatively, try with the approach proposed here:
https://www.endpoint.com/blog/2016/11/16/connect-multiple-jpa-repositories-using
Personally however I would throw JPA out of the window completely as it's anything but easy to dynamically configure arbitrary DataSource objects and reconfigure the repositories to work each time against a different DataSource and the above solution will force you to constant maintenance over such a simple application.
What I would do would be sticking with NamedParameterJdbcTemplate initialising it by using JndiTemplate. Example:
void createUser(String username, String password, String database) {
DataSource ds = (new JndiTemplate()).lookup(database);
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate();
Map<String, Object> params = new HashMap<>();
params.put("USERNAME", username);
params.put("PASSWORD", password);
npjt.execute('create user :USERNAME identified by :PASSWORD', params);
}
List<Map<String, Object>> listUsers() {
DataSource ds = (new JndiTemplate()).lookup(database);
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate();
return npjt.queryForList("select * from all_users", new HashMap<>());
}
Provided that your container has the JNDI datasources already defined, the above code should cover both the creation of a user and the listing of users. No need to define entities or repositories or anything else. You don't even have to define your datasources in a spring #Configuration. The above code (which you will have to test) is really all you need so you could wire it in a #Controller and be done with it.
If you don't use JNDI it's no problem either: you can use HikariCP to define your datasources, providing the additional arguments as parameters.
This solution will work no matter how many different datasources you have and won't need redeployment unless you really have to work on its features. Plus, it doesn't need the developer to know JPA and it doesn't need to spread the configuration all over the place.
Would like to hear experts on best practice of editing JPA entities from JSF UI.
So, a couple of words about the problem.
Imagine I have the persisted object MyEntity and I fetch it for editing. In DAO layer I use
return em.find(MyEntity.class, id);
Which returns MyEntity instance with proxies on "parent" entities - imagine one of them is MyParent. MyParent is fetched as the proxy greeting to #Access(AccessType.PROPERTY):
#Entity
public class MyParent {
#Id
#Access(AccessType.PROPERTY)
private Long id;
//...
}
and MyEntity has the reference to it:
#ManyToOne(fetch = FetchType.LAZY)
#LazyToOne(LazyToOneOption.PROXY)
private MyParent myParent;
So far so good. In UI I simply use the fetched object directly without any value objects created and use the parent object in the select list:
<h:selectOneMenu value="#{myEntity.myParent.id}" id="office">
<f:selectItems value="#{parents}"/>
</h:selectOneMenu>
Everything is rendered ok, no LazyInitializationException occurs. But when I save the object I recieve the
LazyInitializationException: could not initialize proxy - no Session
on MyParent proxy setId() method.
I can easily fix the problem if I change the MyParent relation to EAGER
#ManyToOne(fetch = FetchType.EAGER)
private MyParent myParent;
or fetch the object using left join fetch p.myParent (actually that's how I do now). In this case the save operation works ok and the relation is changed to the new MyParent object transparently. No additional actions (manual copies, manual references settings) need to be done. Very simple and convenient.
BUT. If the object references 10 other object - the em.find() will result 10 additional joins, which isn't a good db operation, especially when I don't use references objects state at all. All I need - is links to objects, not their state.
This is a global issue, I would like to know, how JSF specialists deal with JPA entities in their applications, which is the best strategy to avoid both extra joins and LazyInitializationException.
Extended persistence context isn't ok for me.
Thanks!
You should provide exactly the model the view expects.
If the JPA entity happens to match exactly the needed model, then just use it right away.
If the JPA entity happens to have too few or too much properties, then use a DTO (subclass) and/or a constructor expression with a more specific JPQL query, if necessary with an explicit FETCH JOIN. Or perhaps with Hibernate specific fetch profiles, or EclipseLink specific attribute groups. Otherwise, it may either cause lazy initializtion exceptions over all place, or consume more memory than necessary.
The "open session in view" pattern is a poor design. You're basically keeping a single DB transaction open during the entire HTTP request-response processing. Control over whether to start a new DB transaction or not is completely taken away from you. You cannot spawn multiple transactions during the same HTTP request when the business logic requires so. Keep in mind that when a single query fails during a transaction, then the entire transaction is rolled back. See also When is it necessary or convenient to use Spring or EJB3 or all of them together?
In JSF perspective, the "open session in view" pattern also implies that it's possible to perform business logic while rendering the response. This doesn't go very well together with among others exception handling whereby the intent is to show a custom error page to the enduser. If a business exception is thrown halfway rendering the response, whereby the enduser has thus already received the response headers and a part of the HTML, then the server cannot clear out the response anymore in order to show a nice error page. Also, performing business logic in getter methods is a frowned upon practice in JSF as per Why JSF calls getters multiple times.
Just prepare exactly the model the view needs via usual service method calls in managed bean action/listener methods, before render response phase starts. For example, a common situation is having an existing (unmanaged) parent entity at hands with a lazy loaded one-to-many children property, and you'd like to render it in the current view via an ajax action, then you should just let the ajax listener method fetch and initialize it in the service layer.
<f:ajax listener="#{bean.showLazyChildren(parent)}" render="children" />
public void showLazyChildren(Parent parent) {
someParentService.fetchLazyChildren(parent);
}
public void fetchLazyChildren(Parent parent) {
parent.setLazyChildren(em.merge(parent).getLazyChildren()); // Becomes managed.
parent.getLazyChildren().size(); // Triggers lazy initialization.
}
Specifically in JSF UISelectMany components, there's another, completely unexpected, probable cause for a LazyInitializationException: during saving the selected items, JSF needs to recreate the underlying collection before filling it with the selected items, however if it happens to be a persistence layer specific lazy loaded collection implementation, then this exception will also be thrown. The solution is to explicitly set the collectionType attribute of the UISelectMany component to the desired "plain" type.
<h:selectManyCheckbox ... collectionType="java.util.ArrayList">
This is in detail asked and answered in org.hibernate.LazyInitializationException at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel.
See also:
LazyInitializationException in selectManyCheckbox on #ManyToMany(fetch=LAZY)
What is lazy loading in Hibernate?
For Hibernate >= 4.1.6 read this https://stackoverflow.com/a/11913404/3252285
Using the OpenSessionInView Filter (Design pattern) is very usefull, but in my opinion it dosn't solve the problem completely, here's why :
If we have an Entity stored in Session or handled by a Session Bean or retrieved from the cache, and one of its collections has not been initialized during the same loading request, then we could get the Exception at any time we call it later, even if we use the OSIV desing pattern.
Lets detail the problem:
Any hibernate Proxy need to be attached to an Opened Session to works correctly.
Hibernate is not offering any tool (Listener or Handler) to reatach the proxy in case his session is closed or he's detached from its own session.
Why hibernate dosn't offer that ? :
because its not easy to identify to which Session, the Proxy should be reatached, but in many cases we could.
So how to reattach the proxy when the LazyInitializationException happens ?.
In my ERP, i modify thoses Classes : JavassistLazyInitializer and AbstractPersistentCollection, then i never care about this Exception any more (used since 3 years without any bug) :
class JavassistLazyInitializer{
#Override
public Object invoke(
final Object proxy,
final Method thisMethod,
final Method proceed,
final Object[] args) throws Throwable {
if ( this.constructed ) {
Object result;
try {
result = this.invoke( thisMethod, args, proxy );
}
catch ( Throwable t ) {
throw new Exception( t.getCause() );
}
if ( result == INVOKE_IMPLEMENTATION ) {
Object target = null;
try{
target = getImplementation();
}catch ( LazyInitializationException lze ) {
/* Catching the LazyInitException and reatach the proxy to the right Session */
EntityManager em = ContextConfig.getCurrent().getDAO(
BaseBean.getWcx(),
HibernateProxyHelper.getClassWithoutInitializingProxy(proxy)).
getEm();
((Session)em.getDelegate()).refresh(proxy);// attaching the proxy
}
try{
if (target==null)
target = getImplementation();
.....
}
....
}
and the
class AbstractPersistentCollection{
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
SessionImplementor originalSession = null;
boolean isTempSession = false;
boolean isJTA = false;
if ( session == null ) {
if ( allowLoadOutsideTransaction ) {
session = openTemporarySessionForLoading();
isTempSession = true;
}
else {
/* Let try to reatach the proxy to the right Session */
try{
session = ((SessionImplementor)ContextConfig.getCurrent().getDAO(
BaseBean.getWcx(), HibernateProxyHelper.getClassWithoutInitializingProxy(
owner)).getEm().getDelegate());
SessionFactoryImplementor impl = (SessionFactoryImplementor) ((SessionImpl)session).getSessionFactory();
((SessionImpl)session).getPersistenceContext().addUninitializedDetachedCollection(
impl.getCollectionPersister(role), this);
}catch(Exception e){
e.printStackTrace();
}
if (session==null)
throwLazyInitializationException( "could not initialize proxy - no Session" );
}
}
if (session==null)
throwLazyInitializationException( "could not initialize proxy - no Session" );
....
}
...
}
NB :
I didn't fix all the possiblities like JTA or other cases.
This solution works even better when you activate the cache
A very common approach is to create an open entity manager in view filter. Spring provides one (check here).
I can't see that you're using Spring, but that's not really a problem, you can adapt the code in that class for your needs. You can also check the filter Open Session in View, which does the same, but it keeps a hibernate session open rather than an Entity Manager.
This approach might not be good for your application, there're a few discussions in SO about this pattern or antipattern. Link1. I think that for most applications (smalish, less than 20 concurrent users) this solution works just fine.
Edit
There's a Spring class ties better with FSF here
There is no standard support for open session in view in EJB3, see this answer.
The fetch type of mappings is just a default option, i can be overriden at query time. This is an example:
select g from Group g fetch join g.students
So an alternative in plain EJB3 is to make sure that all the data necessary for rendering the view is loaded before the render starts, by explicitly querying for the needed data.
Lazy Loading is an important feature that can boost performance nicely. However the usability of this is way worse than it should be.
Especially when you start to deal with AJAX-Requests, encountering uninitialized collections, the Annotation ist just usefull to tell Hibernate don't load this right away. Hibernate is not taking care of anything else, but will throw a LazyInitializationException at you - as you experienced.
My solution to this - which might be not perfect or a nightmare over all - works in any scenario, by applying the following rules (I have to admit, that this was written at the very beginning, but works ever since):
Every Entity that is using fetch = FetchType.LAZY has to extend LazyEntity, and call initializeCollection() in the getter of the collection in question, before it is returned. (A custom validator is taking care of this constraints, reporting missing extensions and/or calls to initializeCollection)
Example-Class (User, which has groups loaded lazy):
public class User extends LazyEntity{
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
#BatchSize(size = 5)
List<Group> groups;
public List<Group> getGroups(){
initializeCollection(this.groups);
return this.groups;
}
}
Where the implementation of initializeCollection(Collection collection) looks like the following. The In-Line comments should give you an idea of what is required for which scenario. The method is synchronized to avoid 2 active sessions transfering ownership of an entity while another session is currently fetching data. (Only appears when concurrent Ajax-Requests are going on on the same instance.)
public abstract class LazyEntity {
#SuppressWarnings("rawtypes")
protected synchronized void initializeCollection(Collection collection) {
if (collection instanceof AbstractPersistentCollection) {
//Already loaded?
if (!Hibernate.isInitialized(collection)) {
AbstractPersistentCollection ps = (AbstractPersistentCollection) collection;
//Is current Session closed? Then this is an ajax call, need new session!
//Else, Hibernate will know what to do.
if (ps.getSession() == null) {
//get an OPEN em. This needs to be handled according to your application.
EntityManager em = ContextHelper.getBean(ServiceProvider.class).getEntityManager();
//get any Session to obtain SessionFactory
Session anySession = em.unwrap(Session.class);
SessionFactory sf = anySession.getSessionFactory();
//get a new session
Session newSession = sf.openSession();
//move "this" to the new session.
newSession.update(this);
//let hibernate do its work on the current session.
Hibernate.initialize(collection);
//done, we can abandon the "new Session".
newSession.close();
}
}
}
}
}
But be aware, that this approach needs you to validate IF an Entity is associated to the CURRENT session, whenever you save it - else you have to move the whole Object-Tree to the current session again before calling merge().
Open Session in View design pattern can be easy implemented in Java EE environment (with no dependency to hibernate, spring or something else out side Java EE). It is mostly the same as in OpenSessionInView, but instead of Hibernate session you should use JTA transaction
#WebFilter(urlPatterns = {"*"})
public class JTAFilter implements Filter{
#Resource
private UserTransaction ut;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try{
ut.begin();
chain.doFilter(request, response);
}catch(NotSupportedException | SystemException e){
throw new ServletException("", e);
} finally {
try {
if(ut.getStatus()!= Status.STATUS_MARKED_ROLLBACK){
ut.commit();
}
} catch (Exception e) {
throw new ServletException("", e);
}
}
}
#Override
public void destroy() {
}
}
I need to let Hibernate auto generate the database starting from the entities, however I want them all UPPERCASE.
This used to work in the past now I have messed up column names with Upper and Lower case letters.
I enabled the
.setProperty("hibernate.hbm2ddl.auto", "create")
to let Hibernate auto generate the database, and I created an UppercaseNamingStrategy.java extending org.hibernate.cfg.ImprovedNamingStrategy.
According to https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html_single/#configuration-namingstrategy
Now I should
You can specify a different strategy by calling
Configuration.setNamingStrategy() before adding mappings:
SessionFactory sf = new Configuration()
.setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
.addFile("Item.hbm.xml")
.addFile("Bid.hbm.xml")
.buildSessionFactory();
org.hibernate.cfg.ImprovedNamingStrategy is a built-in strategy that
might be a useful starting point for some applications.
Configuration.setNamingStrategy() however seems to be no more in Hibernate 5.0.6.
I would of course like to do it programatically (I don't have .xml configuration files and don't want them).
NOTE:
Using
.setProperty("hibernate.ejb.naming_strategy", "my.project.hibernate.UppercaseNamingStrategy")
doesn't work as well, seems to be ignored altogether...
Hibernate 5 uses two new interfaces for name strategies PhysicalNamingStrategy and ImplicitNamingStrategy. You need just implement PhysicalNamingStrategy. It is called by Hibernate after all column names are created for model. So you can just make it uppercase. Hibernate uses by default PhysicalNamingStrategyStandardImpl, that do nothing. You can just extend it
public class UpperCaseNamingStrategy extends PhysicalNamingStrategyStandardImpl {
#Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
StringUtils.upperCase(name.getText(), Locale.ENGLISH));
}
}
You can build session factory with UpperCaseNamingStrategy by this way
Configuration configuration = new Configuration();
configuration.setPhysicalNamingStrategy(new UpperCaseNamingStrategy());
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
I am working on a more complex name strategy now. You can refer Hibernate5NamingStrategy if you are interested.
I am using EclipseLink for Single Table Multi-Tenancy.
I have annotated entities with #Multi-Tenant and everything is working fine.
Is it possible to turn off Multi-Tenancy without any change?.
I know there is a org.eclipse.persistence.annotations.Multitenant.includeCriteria() which I can set turn off to disable Multi-tenancy.
But my solution is packaged and delivered as a Package mode and there is no any way to set above attribute to false.
What I want, even I annotated Entity with #Multi-Tenant annotation and even there is a column TENANT_ID in database,
but if I does not set PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT than it should not throw below exception and any CRUD operation should be succeed.
javax.persistence.PersistenceException: Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.QueryException
Exception 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 Entity Manager, Entity Manager Factory or persistence unit properties. If using native EclipseLink, these properties should be set directly on the session.
So, is there any way or any customization/extension point(s) so that If I set
PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT
then It should include TENANT_ID as an criteria and if I not set than simply it should not include TENANT_ID as an criteria?
This seems to be an open issue for eclipselink. I have posted a similar question lately. Chris posted an helpful answer with a link to a feature request. A workaround described there is the usage of multiple persistence units for the same tables with different multi tenancy settings.
I am trying to use an alternative approach: Instead of #Multitenant I use #AdditionalCriteria. This annotations enables you to define your own request criteria added to any database operation (with few exceptions like native SQL).
Example:
#AdditionalCriteria(":ADMINACCESS = 1 or this.tenant=:TENANT")
On creation of your entity manager you set the parameters. You could either provide a tenant of switch off multi tenancy. The drawback here is that you are responsible for the tenant attribute.
There is another workaround to this problem.
You can use an Eclipselink MetadataSource to override at runtime the annotations in the entity class and to pretend that you use a dedicated database per tenant, for example:
MultitenantMetadata md = new MultitenantMetadata();
md.setIncludeCriteria(false);
md.setType(MultitenantType.TABLE_PER_TENANT.name());
TenantTableDiscriminatorMetadata td = new TenantTableDiscriminatorMetadata();
td.setType(TenantTableDiscriminatorType.SCHEMA.toString());
td.setContextProperty(DISCRIMINATOR_SCHEMA_PROPERTY);
md.setTenantTableDiscriminator(td);
entityAccessor.setMultitenant(md);
The above will make Eclipselink happy from a multi-tenancy point of view, but your tables may still have the discriminator column if your annotations specify that multi-tenancy is achieved using a discriminator column.
To remove the discriminator column you can use a SessionCustomizer that manipulates the EclipseLink representation of the entities before the EMF is created in order to:
Remove the discriminator column
Change indices and foreign keys to remove any reference to the discriminator column.
In this way the database tables and the DDLs will no more include the discriminator column.
Set<String> discriminatorColumnNames = getDiscriminatorColumns(descriptor);
for (DatabaseTable databaseTable : descriptor.getTables()) {
List<IndexDefinition> indexDefinitions = databaseTable.getIndexes();
for (Iterator<IndexDefinition> definitionIt = indexDefinitions.iterator(); definitionIt.hasNext();) {
IndexDefinition indexDefinition = definitionIt.next();
// remove qualifier from index to prevent a syntax error in DDL
indexDefinition.setQualifier("");
for (Iterator<String> fieldNameIt = indexDefinition.getFields().iterator(); fieldNameIt.hasNext();) {
if (discriminatorColumnNames.contains(fieldNameIt.next().toUpperCase())) {
fieldNameIt.remove();
}
}
if (indexDefinition.getFields().isEmpty()) {
definitionIt.remove();
}
}
Map<String, List<List<String>>> uniqueConstraints = databaseTable.getUniqueConstraints();
for (Entry<String, List<List<String>>> uc : uniqueConstraints.entrySet()) {
List<List<String>> strings = uc.getValue();
for (List<String> list : strings) {
String found = null;
for (String string : list) {
if (discriminatorColumnNames.contains(string.toUpperCase())) {
found = string;
break;
}
}
if (found != null) {
list.remove(found);
}
}
}
}
I am using JPA in a JavaSE application and wrote a class to manage database connections and persist objects to the database.
The connection parameters for the database connection are passed using the class constructor and the class has a method to validate the connection parameters.
public class DatabaseManager{
private EntityManagerFactory entityManagerFactory = null;
public DatabaseManager(String connectionDriver, String connectionUrl, String username, String password) {
Properties props = new Properties();
props.put("javax.persistence.jdbc.driver", connectionDriver);
props.put("javax.persistence.jdbc.url", connectionUrl);
props.put("javax.persistence.jdbc.user", username);
props.put("javax.persistence.jdbc.password", password);
entityManagerFactory = Persistence.createEntityManagerFactory("name of persistence unit", props);
}
public boolean checkConnection(){
try{
entityManagerFactory.createEntityManager();
}catch(Exception e){
return false;
}
return true;
}
}
When I call the checkConnection method it tries to create a new entitymanager with the given parameters. If no connection can be established the entitymanagerfactory throws an exception and the method returns false.
When I test the method I can see the following results:
All parameters are correct -> the method returns true as expected.
The URL or the username are not correct -> the method returns false as expected.
The drivername or the user password are not correct -> the method returns true but it should return false. <- This is my problem.
Can someone tell me why it behaves like this and what is a proper way to test connection parameters without writing data to some database tables?
At the moment I am using EclipseLink but I'm looking for some provider independent way.
Thanks for your answers.
Creating an EntityManager doesn't have to create any connection at that point. The JPA implementation could delay obtaining the connection until the first persist or flush for example (the JPA spec won't define when a connection has to be obtained).
Why not just use a simple few lines of JDBC to check your DB credentials? At least that way it works independent of how the JPA implementation has decided to handle connections (and those JPA properties have "jdbc" in the name since that is almost certainly what the JPA implementation is using itself to obtain connections).
From JPA 2.0 Specification Section 8.2.1
The persistence-unit element consists of the name and transaction-type attributes and the following sub-elements: description, provider, jta-data-source, non-jta-data-source, mapping-file, jar-file, class, exclude-unlisted-classes, shared-cache-mode, validation-mode, and properties.
The name attribute is required; the other attributes and elements are optional. Their semantics are described in the following subsections.
Entity Manager doesn't need a connection to be created as it ONLY needs the Persistence Unit name, I think you should go with DataNucleus suggesion to make a simple JDBC connection to validate your connection.