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.
Related
All I have implemented multitenancy with mysql and hibernate but I have doubts that it will work in real world.
As per following quote from hibernate document it should be possible:
Connections could point to the database itself (using some default schema) but the Connections would be altered using the SQL SET SCHEMA (or similar) command. Using this approach, we would have a single JDBC Connection pool for use to service all tenants, but before using the Connection it would be altered to reference the schema named by the “tenant identifier” associated with the currently logged in user.
Here is the link from where I got above paragraph.
Multitenancy in hibernate
So I override the MultiTenantConnectionProvider as below
#Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
Connection tenantSpecificConnection = dataSource.getConnection();
if (!StringUtils.isEmpty(tenantIdentifier)) {
Statement statement = tenantSpecificConnection.createStatement();
statement.executeQuery("use " + tenantIdentifier);
statement.close();
tenantSpecificConnection.setSchema(tenantIdentifier);
} else {
tenantSpecificConnection.setSchema(Constants.DEFAULT);
}
return tenantSpecificConnection;
}
It is very basic iteration, a first one, I am just able to switch the database. But with this I also have questions. Would this work in real world? I think multiple users will cause trouble while using this? According to hibernate documentation it should not but it looks it may cause problems. Has anyone tried this, please need help on this one.
What happens when committing a transaction from the entity manager that doesn't contain any dirty object? Can it be that no COMMIT command is sent to the DB?
I was having some test cases failing now and then without a reasonable cause. After some investigation, I have now a theory which I would like to have confirmed here.
I have a small fixture framework to prepare the data on the DB for each test. The fixtures use such a method to store objects to the DB using JPA (Hibernate):
public <R> R doInTransaction(final Function<EntityManager, R> whatToDo) {
final EntityManager em = emf.createEntityManager();
final R result;
try {
try {
em.getTransaction().begin();
result = whatToDo.apply(em);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
}
} finally {
em.close();
}
return result;
}
So, the fixture calls this method passing the whatToDo function where objects are persisted and the method wraps a transaction around the passed function. My failing test cases are using a fixture that relies on legacy code that uses stored procedures and store the objects directly via JDBC, i. e. instead of using em.persist(), I use the following in the passed function to call the stored procedures:
em.unwrap(Session.class).doWork(connection -> {
// stored procedures are called here directly over JDBC
});
So, my theory is that JPA on this circumstance is not immediately committing as there are no JPA dirty objects managed by the EntityManager. Consequently, the actual commits occurs only later, i. e. after the assertion of my test and the test fails. Could it be?
What is the transactional behaviour of Hibernate when "unwrapping" the connection out of the EntityManager?
I've added now an em.flush() before the em.getTransaction().commit() and it seems to help, but I'm still not 100% confident that this solves the issue. Can somebody confirm?
The behavior is the same regardless of unwrapping the connection. if you are not using JTA, the alternative is the underlying transaction provided by JDBC, i.e. local transaction. (or you can implement your own managed transaction provider)
When you unwrap the connection and deal with JDBC directly, you still get the same connection that is initially obtained by this session/entity manager. So it's the same effect.
Consider a situation where all client data is stored in its own database/catalog and all such databases are stored in a single RDBMS (client-data). Master data (e.g. clients, ...) is kept in another RDBMS (master-data). How can we dynamically access a particular database in client-data RDBMS by means of JdbcTemplate?
Defining DataSource for each database in client-data RDBMS and then dynamically select one as suggested here is not an option for us since the databases are created and destroyed dynamically.
I would basically need something like JDBC's Connection.setCatalog(String catalog) but I have not found anything like that available in Spring JdbcTemplate.
Maybe you could wrap the datasource with DelegatingDataSource to call setCatalog() in getConnection() and use the wrapped datasource on JdbcTemplate creation:
class MyDelegatingDS extends DelegatingDataSource {
private final String catalogName;
public MyDelegatingDS(final String catalogName, final DataSource dataSource) {
super(dataSource);
this.catalogName = catalogName;
}
#Override
public Connection getConnection() throws SQLException {
final Connection cnx = super.getConnection();
cnx.setCatalog(this.catalogName);
return cnx;
}
// maybe also override the other getConnection();
}
// then use like that: new JdbcTemplate(new MyDelegatingDS("catalogName", dataSource));
You can access the Connection from JdbcTemplate:
jdbcTemplate.getDataSource().getConnection().setCatalog(catalogName);
You'll only have to make sure the database driver supports this functionality.
jdbcTemplate.getDataSource().getConnection().setSchema(schemaName)
Was what I needed for switching schema using postgres. Props to #m3th0dman for putting me on the right track. I'm only adding this in case others find this answer searching for switching schema as I was.
I created the entity classes of my database with NetBeans. I believe I know how to use EntityManager
(Class) db.createNamedQuery(String name);
but I don't know where to put the statement (database link, login, password) so I learned how to query with
(JDBC4ResultSet) statement.executeQuery(String query);
but it returns the set which I don't know how to transform into Entity class... something like
(Class) statement.execureQuery(String query).toEntity(Class);
would be nice. ;-)
Ok, First you need to get an EntityManager from entity manager factory with your persistance unit name (which will be configured in persistance.xml). And then you create an EntityManager.
EntityManagerFactory emf=Persistence.createEntityManagerFactory("persistance_unit_name");
EntityManager em=emf.createEntityManager();
Query query = em.createNamedQuery("namedQueryName"); //this returns a query
List<ENTITIY> result = query.getResultList();
This is just an heads up, You can google 'jpa example' to find out more working examples.
I have one persistence unit configured in my persistence.xml but i have two databases. Those databases are identical, regarding the schema. What i am trying to do is:
Persistence.createEntityManagerFactory("unit", primaryProperties);
Persistence.createEntityManagerFactory("unit", secondaryProperties);
The properties contain different connection settings (user, password, jdbc url, ...).
I tried this actually and it seems that hibernate (my jpa provider) returns the same instance in the second call, without taking care of the properties.
Do i need to copy the configuration to a second unit?
I nailed it down to something different than i thought before. The EntityManagers (and Factories) returned by the calls above work as expected, but getDelegate() seems to be the problem. I need to get the underlying session to support legacy code in my application which relies directly on the hibernate api. What i did is:
final Session session = (Session) manager.getDelegate();
But somehow i receive a session operating on the primary database even when using an entitymanager which operates on the second.
This is weird. According to the sources of HibernateProvider#createEntityManagerFactory, the method returns an new instance:
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) {
Ejb3Configuration cfg = new Ejb3Configuration();
Ejb3Configuration configured = cfg.configure( persistenceUnitName, properties );
return configured != null ? configured.buildEntityManagerFactory() : null;
}
And I definitely don't get the same instances in this dummy test:
#Test
public void testCreateTwoDifferentEMF() {
Map properties1 = new HashMap();
EntityManagerFactory emf1 = Persistence.createEntityManagerFactory("MyPu", properties1);
Map properties2 = new HashMap();
properties2.put("javax.persistence.jdbc.user", "foo");
EntityManagerFactory emf2 = Persistence.createEntityManagerFactory("MyPu", properties2);
assertFalse(emf1 == emf2); //passes
}
Actually, it just works (and the second instance is using the overridden properties).