I have a service method on an api that can be called to check the health of my database connection.
The method is pulling the query string from a properties file (depends on DB vendor, using Sybase and HSQL for now, more in future), and executing it. Then the method lets the caller know if it succeeded or failed.
In addition to this, I was using the Query.setHint("javax.persistence.query.timeout") to set a timeout on the query:
javax.persistence.EntityManager entityManager;
...
Query heartbeatQuery = entityManager.createNativeQuery(heartbeatQueryString);
heartbeatQuery.setHint("javax.persistence.query.timeout", heartbeatTimeout);
heartbeatQuery.getResultList();
My problem is the timeout property is working against my Sybase DB, but not against my HSQL DB. It sounds like it depends on the vendor, so I don't know for sure when it will work.
Is there a better way to generically test the DB connection & include some kind of timeout parameter?
Well sadly no. JPA's query hints are not mandatory, i.e. it's up to the implementator (EclipseLink, Hibernate, etc) to enforce them or not. Moreover, even if the implementator does chose to recognize a certain query hint, if that hint's functionality is not supported by the database then it won't work (here some implementators are nice and tell you if a certain hint won't work agains the current db while others fail silently). In the case of HSQLDB there's no way to set the query timeout. You can only set a timeout for the login (i.e. how long should it wait for a successful login before failing), but not for the queries duration.
Things are not so grim however. On the one hand, even if you'd solve this, you'd still stumble over other issues with HSQLDB, as it does not support a lot of other nice functionalities that most dbs have. You should only use HSQLDB for basic integration/unit testing. For more involved testing, you can use the integrated MySQL Java library. You can find it here:
http://dev.mysql.com/doc/refman/5.0/en/connector-mxj.html
This is simply a packaged fully working Mysql server, which has a Java api for star and stop, works on most major OSs (win,lin, os x, etc). This way you can have your integration tests start a real Mysql server, and try your code there, where such stuff as a query timeout hint will work fine.
Related
There were some proposed solutions to the question "How to test SQL statements in an application" -
Using RAM memory - I can't change the configuration of staging environment where testing happens.
Using H2 - Not very compatible even in PostgreSQL mode
Use the same database to run the tests.
Using in-memory mode - PostgreSQL doesn't have one.
The third one was viable and I looked into Test Containers which is actually a beautiful solution but a relatively new one. As a result, our company is sceptical of adopting it.
We use Mybatis to access PostgreSQL.
Another way would be to recreate entire schema and populate required tables before tests. Here is the problem, I could create and delete schema with tables with the same name. To avoid name collision I'd have to change schema's name, as a result, even queries should be renamed which is not at all preferred. Is there a way to do this without changing queries but pointing them to the dummy schema.
You should NOT change your queries. In tests you should only change the connection url your application will use. The problem is, how to get that url working.
To have full test coverage you need the same db (as you noticed, h2 and other in-memory db are not very compatible). postgres doesn't have in-memory mode so you have to manage the lifecycle yourself. there is a few decisions you have to make. some of them:
where will you get the db from: require all the devs to provide postgres (installation / docker / vagrant) or automate the setup?
how to prepare db for tests: manual schema setup and cleanup?
how to reset db between tests: restart? always rollback? predefined and separately defined content? some kind of reverse operations?
if and how to make those tests fast?
there are some tools that can help you solve some of the problems:
testcontainers will help you provide
db.
dbunit - will help you prepare data for your test.
cons:
a lot of work is required to create and maintain schema and data. especially when your project is in a intensive development stage.
it's another abstraction layer so if suddenly you want to use some db feature that is unsupported by this tool, it may be difficult to test it
testegration - intents to provide you full, ready to use and extensible lifecycle (disclosure: i'm a creator).
cons:
free only for small projects
very young project
you can also fill the gaps on your own. as always it's a trade: time vs money
you can define database configuration for test purpose and connect to your real database base for execute tests. you should access to test database configuration in test classes.
for example, if you use spring and hibernate to connect to the database, you can define a test hibernate configuration xml file where it connect to test database. then in your test classes, use this configuration file as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguratiion({testHibernate.xml, testSpring.xml , .... })
#TestExecutionListeners({...})
public class TestClass {
....
#Test
public void test1(){
...
}
}
so, you can access your test hibernate session factory to execute your queries.
Earlier I was trying to get batch inserts working in Hibernate. I tried everything: For the config I set batch_size(50), order_inserts(true), order_updates(true), use_second_level_cache(false), use_query_cache(false). For the session I used setCacheMode(CacheMode.IGNORE) and setFlushMode(FlushMode.MANUAL). Still the MySQL query log showed that each insert was coming in separately.
The ONLY thing that worked was setting rewriteBatchedStatements=true in the JBDC connection string. This worries me, as my application is supposed to support any JBDC database and I'm trying to avoid DB specific optimizations.
Is the only reason hibernate can't actually use batch statements because the MySQL driver doesn't support them by default? What about other drivers, do I have to add options to the connection string so they can support batched inserts? If you need specific db's, think SQL server, sqlite, Postgres, etc
One reason it could not be working is that hibernate disables batching if you use the Identity id generation strategy.
Also MySQL doesn't support JDBC batch prepared statements the same way as other databases without turning on the rewrite option.
I don't see that it is a problem to turn this flag on though, if your are setting up your application for a different database you will have to change the settings such as dialect, driver name, etc. anyway and as this is part of the JDBC connect String then you are isolated from the configuration.
Basically I think you are doing the right thing.
As batch insert (or bulk insert) is part of the SQL standard, ORM frameworks like Hibernate support and implement it. Please see Chapter 13. Batch Processing and Hibernate / MySQL Bulk insert problem .
Basically, you need to set the JDBC batch size via the variable named hibernate.jdbc.batch_size to a reasonable size. Also don't forget to end the batch transaction with flush() and clear().
Actually when running Selenium test case. I takes the live (Main Database) database which is configured in Glassfish. If we add records for testing purpose then it will replicate in the Main database. This is not good know. So is there any way to have separate DataBase for Selenium test case.
For Glassfish:
Define your JDBC Connection Pool resource to refer to different databases for your production server vs your development server.
If you're not running two different servers, then your first step is to fix that, and be running different servers. And different database servers. Never point your development machine/server at production data, or even the production database server.
If you are trying to do a unit test that should run on deployment, then you must create a second connection in your unit test program.
You might want to use an in memory database for this, maybe HSQL which comes bundled with the java sdk so you don't need to install any drivers and also you won't need to cleanup anything after the tests are run.
How you create the second connection depends on what you are using, hopefully you will have a central class or method to get the database connections, something like this:
Connection c = MyConnectionClass.getConnection();
so you will have it easy modifying the getConnection method so you can point out to the HSQL direct connection for your tests, with something like this:
public Connection getConnection(){
if(testing){
Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:mymemdb", "SA", "");
}else{
//get your connection from your pool or whatever you are doing right now
}
}
or also you can be more correct and create a mock for this class. But I'm not sure if selenium supports this out of he box.
And of course, you will need to create your database schema into the in memory database before beginning the tests. If you use hibernate or JPA for example then that should be simple (be sure that you add a second persistence unit and use that in this case), if not then you should have the scripts for your database and run them with JDBC as you would run them in any database.
Also if you don't want to run the scripts every time the tests are run use hsql but in file mode (change the jdbc:hsqldb:mem for jdbc:hsqldb:file it will use a file to store the database).
here's some info about the hsql database if you want to know more: link
Also if you dont like hsql you can try sqlite (only for file mode) which I think has more tools for accessing it
I have a scenario where the unit of work is defined as:
Update table T1 in database server S1
Update table T2 in database server S2
And I want the above unit of work to happen either completely or none at all (as the case with any database transaction). How can I do this? I searched extensively and found this post close to what I am expecting but this seems to be very specific to Hibernate.
I am using Spring, iBatis and Tomcat (6.x) as the container.
It really depends on how robust a solution you need. The minimal level of reliability on such a thing is XA transactions. To use that, you need a database and JDBC driver that supports it for starters, then you could configure Spring to use it (here is an outline).
If XA isn't robust enough for you (XA has failure scenarios, such as if something goes wrong in the second phase of commits, such as a hardware failure) then what you really need to do is put all the data in one database and then have a separate process propagate it. So the data may be inconsistent, but it is recoverable.
Edit: What I mean is that put the whole of the data into one database. Either the first database, or a different database for this purpose. This database would essentially become a queue from which the final data view is fed. The write to that database (assuming a decent database product) will be complete, or fail completely. Then, a separate thread would poll that database and distribute any missing data to the other databases. So if the process should fail, when that thread starts up again it will continue the distribution process. The data may not exist in every place you want it to right away, but nothing would get lost.
You want a distributed transaction manager. I like using Atomikos which can be run within a JVM.
I'm using an object/relational mapper to talk to the database. The library in my case is iBatis which is also used for transaction management.
However, I recently had a case where iBatis didn't actually start a transaction even though startTransaction() etc. was called as documented.
After some debugging I found out that there was a configuration mistake on my side.
One might blame iBatis but I would like to avoid such misconceptions in the future.
So here's the question:
How can I programmatically assert that the current database connection is running in a transaction?
The databases I'm using are Oracle, MySQL and H2 (for testing).
I'm not 100% sure if this is absolutely indicative of being in a tx, but Connection.getAutoCommit() tells you if the connection is in auto-commit mode, where auto-commit "on" means "no transaction".
There may well be cases where this assertion does not hold, but most JDBC-based frameworks will use that setting to control transactions.