Spring Transaction handling with EclipseLink - java

I am new to databases and transactions. I find a lot of different information up-to-date and am currently trying to organize my thoughts.
Regarding the context, I'm trying to test the current isolation level with the SQL Server, but I don't succeed.
For this I use Spring Transaction together with Eclipse Link. I found some information about Spring transactions (https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth). However, there is still a concept of the works unit and now I don't know what is currently being used (https://wiki.eclipse.org/Introduction_to_EclipseLink_Transactions_(ELUG)#Unit_of_Work_Architecture)
What I am trying to test:
I have an Entity User (id, firstname, lastname). I have an entry in table id = 1, firstname = foo lastname = bar
I have a service and transaction. IMHO default is isolation READ_COMMIT for SQL Server
#Transactional
public User updateUser(){
User updateUser = new User(1, "new firstname", "new lastname");
User updatedUser = userRepository.save(updateUser); --> em.merge
return updatedUser;
}
So far so good. What I do not understand now. I set a breakpoint in the return.
At the same time I opened a second SQL client and executed the following SQL.
SET IMPLICIT_TRANSACTIONS ON
UPDATE
User
SET
lastname = 'complete new',
WHERE
id = 1
COMMIT TRAN
What I would expect is that the SQL statement will wait for the Spring transaction to complete. BUT this is currently not the case, the SQL statement is simply carried out.
Then lastname is in the table lastname "complete new" then I resume the breakpoint and then the lastname is "new lastname". This behavior I cannot understand. Is this normal or is this because of the unit work of eclipse link?

EclipseLink buffers SQL statements that need to be executed for as long as possible in order to reduce the lock time in the RDBMS. In your particular case the JDBC driver will receive the UPDATE statement when Spring Data JPA commits the transaction. You can verify it by enabling SQL logging in EclispeLink: stackoverflow.com/q/2374395/17695211
After enabling SQL logging you'll see that there won't be any SQL debug output in the console at your breakpoint. It will appear after the return. If you really want to see the locking effect, you need to write the repository without Spring Data JPA with a #PersistenceContext-injected EntityManager and call EntityManager.flush which will flush SQL statement buffer of EclipseLink to the JDBC driver before the breakpoint.
Alternatively (and if you feel adventurous), you may try looking for a place in the EclipseLink source code where it executes the corresponding PreparedStatement, and set the breakpoint right after it.

Related

Quartz scheduler not executing the SQL Query in a dropwizard application

I've an application created using dropwizard framework where I've registered a quartz-scheduler job scheduled to run after every specified duration. This job fires a SQL query to SQL Server DB and iterates the ResultSet and sets the data to a POJO class which is later pushed to a queue.
The SQL query has UNION joining multiple tables which fetches the data for the records modified in a delta time using the last_modified_time column of the related table in where clause. DB jar included in pom.xml is sqljdbc-4.4.0 and quartz version is 2.2.1
The query looks like this:
SELECT
u.last_modified_date,
u.account_id,
u.user_id,
ud.is_active
FROM user u WITH (NOLOCK)
JOIN user_details ud with (NOLOCK) ON u.account_id = ud.account_id AND u.user_id = ud.user_id
WHERE u.last_modifed_date > ? AND ud.last_modifed_date <= ?
UNION
SELECT
u.last_modified_date,
u.account_id,
u.user_id,
ud.is_active
FROM user u WITH (NOLOCK)
JOIN user_details ud with (NOLOCK) ON u.account_id = ud.account_id AND u.user_id = ud.user_id
JOIN user_registration_details urd WITH (NOLOCK) ON urd.account_id = u.account_id AND urd.user_id = u.user_id AND urd.reg_id = ud.reg_id
WHERE urd.last_modifed_date > ? AND urd.last_modifed_date <= ?
This query is called by simple connection statement and resultset like this
final ManagedDataSource datasource configuration.getDatabase().build(environment.metrics(), "sql");
// configuration is the configuration class in a drop wizard application and configuration.getDatabase() returns
// the DataSourceFactory with all credentials like user, password and url set into it
try (Connection conn = dataSource.getConnection()) {
int resultSetType = SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY;
int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
LOGGER.info("Starting execution: ");
try (PreparedStatement pstmt = conn.prepareStatement(getQuery(), resultSetType,resultSetConcurrency))
{
setQueryParameters(pstmt);
try (ResultSet rs = pstmt.executeQuery();)
{
//process results
}
}
} catch (SQLException | IOException ex) {
LOGGER.error(“Error occurred “ + ex);
}
LOGGER.info("Completed execution: ");
In a simple execution, it prints the logs "Starting execution" and then processes the records and prints "completed execution". But sometimes during the execution, it's printing the logs "Starting execution" and "completed execution" but this query is not actually fired to the SQL DB.
As I didn't get the records which I modified in that delta time, I put the profiler to check if the query is actually fired and didn't found this query firing to the DB. Also, I've tried adding log4jdbc library http://code.google.com/p/log4jdbc/wiki/FAQ to print the query to the logs but no logs were printed for this query.
with (NOLOCK) is not MySQL syntax. Look at the settings in the wizard and see if you have specified the correct RDBMS engine. In particular, it sounds like SQL Server syntax.
The equivalent may involve setting the TRANSACTION ISOLATION LEVEL to something like READ UNCOMMITTED.
I turned the SQL Profiler on this query and filtered it for my server to check if the query was actually hitting the DB from my application and found that the profiler could only find it rarely hitting the DB. So, I thought there might be some caching happening at mybatis level. Then I added more logs and performed debug analysis on mybatis by enabling all kind of logging and to check if there are any caching at local level or second level cache mybatis configuration but that wasn't the cause.
Then I used spy jdbc driver log4jdbc driver to log all the queries, parameters and all db information requests at db level.
My organization is using Spunk application to display logs from all the applications deployed to the different host servers. While checking the logs in splunk, I observed the same query was printed twice in the logs but when I noticed deeply it was printing one from my instance and another execution from the different instance deployed on some other server. I logged in to that server and found the same application deployed over there not updated since months. It was found to be multiple application instances running in the same environment but on two different servers and there is no way I could figure out that an application was deployed on multiple hosts.
Thank you #halfer for all the help and bounty.

Is it possible to merge multiple insert statements to 1 insert statement in hibernate with postgres db?

For the purpose of optimizing big insert processes in our system, we intend to batch in our insert sql statements. But no matter what I configure, so far, I'm not able to accomplish that.
Based on the answer on this SO question:
Hibernate batch size confusion
It should be possible.
I already configured the hibernate.jdbc.batch_size property but still was not able to batch the insert statements? I can confirm this by enabling sql logs on both hibernate side and the postgres server side.
Is it possible to merge multiple insert statements to 1 insert statement in hibernate with postgres db?
Sample entity I used below:
class GenericMessage {
String name
//other fields
}
I'm using GORM, so it's on groovy (These are done in a transaction):
GenericMessage message1 = new GenericMessage(name: 'name1').save()
GenericMessage message2 = new GenericMessage(name: 'name2').save()
session.flush()
session.clear()

How do I set the isolation level of transaction of Postgres from Java?

I have some stored procedures, and I have a class through which I call them.But before I call them I want to change the isolation level from the read commited to repeatable read in the class trough which I call them (or) while establishing a connection to the database(not sure if its possible)(in java) using the Postgres jdbc.
I tought I could establish the isolation level in the stored procedures themselves,but after some looking around,I found that its out of question.
So how do I mention the isolation levels from the java side before calling the procedures?
I know in psql I would do the following :
set transaction isolation level repeatable read;
begin transaction
--my transactions begin...--
Sorry if this is a naive question,but I havn't been able to find an answer to my question.
EDIT__
I call my stored procedure by the following
String query = String.format("SELECT create_queue('%s')", queueName);
st = connection.createStatement();
rs = st.executeQuery(query);
create_queue is my stored procedure.
Use Connection::setTransactionIsolation
http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setTransactionIsolation-int-

Oracle: Read-only access to the schema for another user?

I have a schema and a user with the same name: products. For development I would like to use it in a read-only mode from a java application. Therefore, I created a new user for the read-only application.
CREATE USER PRODUCTS_READONLY IDENTIFIED BY PRODUCTS_READONLY;
GRANT CREATE SESSION to PRODUCTS_READONLY;
BEGIN
FOR tab IN (SELECT table_name FROM all_tables WHERE owner = 'PRODUCTS') LOOP
EXECUTE IMMEDIATE 'GRANT SELECT ON PRODUCTS.'||tab.table_name||' TO PRODUCTS_READONLY';
END LOOP;
END;
Running the application I got the error that the table did not exist. Searching on the internet for solution, I came across SYNONYM. So I added synonym to the schema:
CREATE SYNONYM PRODUCTS_READONLY FOR PRODUCTS;
Now, I am getting this error in my java application:
ERROR org.hibernate.util.JDBCExceptionReporter - ORA-17074 invalid name pattern.: PRODUCTS_READONLY.PRODUCT_TYPE
What is wrong with my approach?
--UPDATE--
Seems like creating synonyms for schema was removed in 10g (Oracle: is it possible to create a synonym for a schema?). If I create schema for each object in the target schema, I would have to do the same every time a table is added to the target schema or any other changes to the any other objects in the target schema? That sounds cumbersome...
--UPDATE 2--
Seems like a trigger with alter session is a possible solution, but will it make the tables read-only so long the user has only SELECT privilege?
If you have control over the way your application connects (e.g. an initialization statement for your connection pool), all you need to do is run:
ALTER SESSION SET CURRENT_SCHEMA = PRODUCTS;
From that point onward (during the lifetime of the session) any unqualified object name will be searched for in the PRODUCTS schema.
All grants given to PRODUCTS_READONLY will be in effect. The session will run under the credentials (and security restrictions) of the original user used to log in.
If you can not change the way the connection is established or initialized a logon trigger should also accomplish this:
create or replace trigger logon_trg
after logon on database
begin
if (user = 'PRODUCTS_READONLY') then
execute immediate 'alter session set current_schema = products';
end if;
exception
when others then null; -- prevent a login failure due to an exception
end logon_trg;
/
Note that it's crucial to trap any exception, because otherwise a potential error in the executed SQL will effectively log everyone out off the database. So use with care and test it well before putting that into production.
I am not sure you can create a synonym for schema.
But you can create a synonym for every object in the remote schema, e.g.
begin
for c in (select t.table_name from table_privileges t where grantee = 'PRODUCTS_READONLY') loop
execute immediate 'create synonym '||c.table_name||' for PRODUCTS.'||c.table_name;
end loop;
end;
Don't be confused with table_name, it handles all types of objects there.

Does Hibernate's createCriteria() sanitize input?

Came across some code today that uses Hibernate to perform a query. The query uses a value submitted from a form. It made me curious as to whether or not this sort of code "sanitizes" its input.
public List<School> search(String query) {
Session session = this.getCurrentSession();
query = "%" + query + "%";
Criteria criteria = session.createCriteria(getPersistentClass());
criteria.createAlias("country", "a");
Criterion nameCriterion = Restrictions.ilike("name", query);
Criterion cityCriterion = Restrictions.ilike("city", query);
Criterion countryCriterion = Restrictions.ilike("a.name", query);
Criterion criterion = Restrictions.or(Restrictions.or(nameCriterion, cityCriterion), countryCriterion);
criteria.add(criterion);
return criteria.list();
}
Is this safe?
Hibernate Criteria Queries are quiet safe in terms of Sql Injection since they pass strings as parameter while performing any fetch. Even, Hql is quiet safe unless you build the query via string literal.
For more details, you should take a look at queries getting fired at the database level by switching on hibernate sql logging.
If you think to SQL injection attacks, then yes, Hibernate Criteria API is safe.
It will generate the underlying query by first compiling it from the specified query fields and only after apply the query parameters (It should use a classical PreparedStatement). This way the JDBC driver will know which part of the query are fields and which part are parameters. Then the driver will take care to sanitize the parameters.
Tough you should take care with the SQL restrictions applied on the Criteria, if you need to place parameters there. For example
String vulnerable = //parameter from user interface
criteria.add(
Restrictions.sqlRestriction("some sql like + vulnerable") //vulnerable
criteria.add(
Restrictions.sqlRestriction("some sql like ?",
vulnerable, Hibernate.STRING)) //safe
In this case the vulnerable parameter could "leak" in to the query fields part and be bypassed by JDBC driver checking as in a normal vulnerable SQL query.
Hibernate is useful to sanitizing inputs but sanitizing inputs is not considered the best practice for preventing SQL injection attacks. As your code develops over time, you will need to remember to change your Hibernate sanitation as your database and client-side application change; this leaves a lot of room for error and any one mistake can compromise your database.
To prevent SQL injection attacks, it is better to use prepared statements. In a prepared statement, your client-side application will make a non-SQL request and let your server generate your SQL statement.
For example, if a user wants all users in the city "Dallas" then your client-side application should make a request similar to username equals "Dallas" and then your server can generate:
SELECT * FROM users WHERE name='Dallas'

Categories

Resources