How to handle the situation where we have a Postgres database running with many database roles (representing the users) and want to use hibernate so every database statement would be executed using a connection fetched with the specific user?
To get a Session/EntityManager we need to fetch it from an EntityManagerFactory, which requires a DB user/password, usually specified in persistence.xml like this:
<property name="javax.persistence.jdbc.user" value="SYSDBA"/>
<property name="javax.persistence.jdbc.password" value="masterkey"/>
Of course i can create a Session/EntityManager for every user using a separate EntityManagerFactory, but this is a costly operation. How can this problem be solved?
If the RDBMS is PostgreSQL I think the best way to accomplish this would be to call the SET ROLE command. This command will change the role and permissions to whatever role is specified. It will carry out all SQL commands during the session as if you logged in with that role in the beginning.
Here is a link to the Postgres documentation.
Related
I am using hibernate 5.6 and I am observing an odd effect I don't Understand. For development I configured c3p0 with maximum number of connections set to 1, in order to detect missing close of sessions.
To test this, I added a Thread.sleep(15000) call just before I close each session, and as expected my website crawls to 1 request each 15 seconds, because each new page can't be shown, until the previous session was closed. So far so good.
But then something odd happens. I have a Servlet, which use the session object without calling beginTransaction() on the session.
If I insert a sleep(30000) call in this servlet after using the session, and just before I close the session the rest of my website does not hang. But If i start a new request to this servlet, this second request, will wait until the first request finishes.
So it is like hibernate does have 2 session pools. One for sessions which use transactions, and one for sessions which don't use transactions. Is this true, and described in the documentation?
My first thought was obviously, that I create 2 different sessionFactories, but logging shows the session factory is the same for all my servlets. And If i modify the servlet to start with a call to beginTransaction() it the rest of the site hangs until the session is closed.
I am using Apache Tomcat and Hibernate, but I don't use Spring, and I have created the session factory object myself.
The relevant from my hibernate.cfg.xml file is:
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.c3p0.max_size">1</property>
Hibernate will acquire the JDBC connection as late as possible. When you use beginTransaction, it has to acquire the connection.
Simply creating a session does not immediately create a JDBC connection though, so a concurrent request that doesn't interact with the session in any way won't block.
In spring if we define method in repository like this:findByName(String name), we could call this method to retrieve data back. What I want is that, could I have some ways to call 2 or more methods like I say above, and spring sends query to database in just one round instead of 2rounds? I would like to optimize performance in the case that I am certain that some sql queries will be sent togother
update: one round means in one connection we send multi sql queries. The object is to avoid more than one round trip time when there is more than 1 sql query is about to send.
e.g., query 1 is select * from table where xx=bb
query 2 is selext * from another_table where zz=cc
in trivial way, we may send 2 queries like this:
1. send query 1 by calling repository's findbyxx method
2. send query 2 by calling repository's findbyzz method
in above case, query 2 will be sent after query 1's response came back. This is a waste IMHW. I am seeking a way to send these 2 queries at once and got answer at once.
If you want to keep the database connection between these two queries, you must set up a transaction manager for your JPA Configuration:
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="yourEntityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
This will imply that when you annotate a #Service's method with #Transaction (or the whole class), the same session/connection will be kept between your queries.
For more info: https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
that's a question which has confuse me a lot.
for example:
when I design the Dao layer,sometimes,I must do some insert operation,and than
I should do some query such as select the data's id by auto-generate in db.
my question was that:
when I use spring to help manage datasource,
when I do more than two sql operation one by one,
how many times the java client connect to the db?? only one ? or more?
code,such as fellows:
getSimpleJdbcTemplate().update(some params...);
getSimpleJdbcTemplate().query(some params...);
It depends on your Transactional settings.
Spring-transactions in local mode, work on a thread-local connection for all the db activities within single transaction.
If you have not configured transactions, then basically each DB call will retrieve connection from datasource using Datasource.getConnection()
In terms client connecting to DB, if you are using datasource with connection pooling capability, then connections are returned from the pool.
But if datasource is not backed by pool, then it will instantiate connection to DB server on demand ( on getConnection() ) call
In my web application I'm using JDBC connectivity basically its working fine with below code
connObj = DriverManager.getConnection(dbaseUrl, usrName, Paswrd);
But when I'm using veracode tool its showing flaw as J2EE Bad Practice:getConnection so that i need to implement.
InitialContext ctx= new InitialContext();
DataSource dsrc=(DataSource)ctx.lookup(dbaseUrl, usrName, Paswrd);
dsrc.getConnection();
How can I pass 3 parameters in lookup so that it should not disturb my previous flow of code. Can anybody guide me please?
You cannot change the lookup method parameter. But you can use bind or rebind methods of InitialContext to retrieve your datasource.See sample
ctx.bind("java:/comp/env/jdbc/nameofmyjdbcresource", dsrc);
For details example check here.
A DataSource is a connection to one database and you configure a DataSource with a single username and password. If you really need specific (and changing) usernames/passwords depending on application logic, then you can use DataSource.getConnection(String username, String password). However keep in mind with some (most?) connection pools this will give you a non-pooled connection.
If you want to access a different database (or a different configuration), then you need to specify a data source for each database you want to access and ask for that specific data source.
If that is not possible for your situation, then you should just ignore/suppress the veracode warning and continue using DriverManager.
We have a FlushEventListener to do audit functionality. While updating some entity, hibernate will callback our audit code just before flushing. The audit code needs to query the database.
If we try to do it in the same session apparently we mess up the session's state: we get a NullPointerException from inside hibernate, at some point when it's validating naturalIds inside a class named NaturalIdXrefDelegate.
We currently solved it by opening a new session for the audit query. The problem with this is we're losing the benefit of getCurrentSession (a session for the whole request, managed by hibernate). This way we're going back to opening one session per query.
Is there an elegant solution for this or we basically need to re-implement getCurrentSession to manage our own session #2 in the request?
You don't have to open new session. It's enough to temporarily disable flush.
Session session = entityManager.unwrap(Session.class);
session.setHibernateFlushMode(FlushMode.MANUAL);
// do your db stuff
session.setHibernateFlushMode(FlushMode.AUTO);
It's actually a lot faster than
session.getSessionFactory().openSession()
Which works to btw.