modifying existing code to adapt nolock in jpa - java

What I want to achieve is, I would like to do a no lock query execution in select statement. But according to this answer it is impossible to achieve this with direct JPA implementation. I also understood from searches that nolock and READ_UNCOMMITTED are same. Is there any way to achieve this(no lock, READ_UNCOMMITTED) by modifying my below code. Or should I use the native query with specifying WITH(NOLOCK)
I had tried
entityManager.createQuery(query).setLockMode(LockModeType.NONE).getResultList();
but it also not solving my issue.
My references this, this , this, this
I am using the following code to get data from table. this code works fine without nolock.
String query = "FROM Employee WHERE empId=:empId AND empStatus='failed'";
to fetch data from db
public Object getListFromQuery(String query) throws Exception {
Object resultObject = null;
List<Object> queryResultList = null;
EntityManager entityManager = null;
try {
entityManager = entityManagerFactory.createEntityManager();
queryResultList = entityManager.createQuery(query).getResultList();
resultObject = (Object) queryResultList;
} catch (Exception ex) {
LOGGER.error("Exception : DatabaseManager :executeQueryGetList ",ex);
throw ex;
} finally {
entityManager.close();
}
return resultObject;
}
Database configuration
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
throws NamingException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean
.setPackagesToScan(new String[] { "com.test.middleware.entity" });
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
factoryBean.setPersistenceUnitName("test_unit");
return factoryBean;
}

Related

spring-boot ConcurrencyFailureException oracle

I have a code, that when an error returns from a procedure, I need to update a table column in oracle. However, at the time of the update (inside the catch block), the following error occurs:
org.springframework.dao.ConcurrencyFailureException: PreparedStatementCallback; SQL [UPDATE TB_XPTO SET COLUMN_XPTO = XX WHERE ID_XPTO = ?]; ORA-02091: transação repetida
; nested exception is java.sql.SQLTransactionRollbackException: ORA-02091:
My code:
try {
jdbcTemplate.update ("call PROCEDURE_XPTO(?)", ID_XPTO);
} catch (Exception e) {
jdbcTemplate.update("UPDATE TB_XPTO SET COLUMN_XPTO = XX WHERE ID_XPTO = ?", idXpto);
}
My data source Config class
#Bean
public DataSourceBuilder<?> dataSourceBuilder(Environment springEnvironment) {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("oracle.jdbc.OracleDriver");
dataSourceBuilder.url("jdbc:oracle:thin:#//server:1521/database.com.br");
dataSourceBuilder.username("user");
dataSourceBuilder.password("pass");
return dataSourceBuilder;
}
#Bean
public DataSource getDataSource(DataSourceBuilder<?> dataSourceBuilder) {
return dataSourceBuilder.build();
}
Any idea ?
thanks for your answer, but worked for me, using #Transactional, in the method that calls the procedure.

Why SELECT SQL executed each time even configured SecondLevel and Query Cache in Hibernate

hi I Configured second level cache/query cache in my spring mvc - hibernate application.
Configuration Part:
#EH Cache
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
hibernate.cache.use_query_cache=true
HibernateProperties:
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.hbm2ddl.auto", environment.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));
properties.put("hibernate.hbm2ddl.auto", environment.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.cache.use_second_level_cache", environment.getProperty("hibernate.cache.use_second_level_cache"));
properties.put("hibernate.cache.region.factory_class",
environment.getProperty("hibernate.cache.region.factory_class"));
properties.put("hibernate.cache.use_query_cache","hibernate.cache.use_query_cache");
// properties.put("", environment.getProperty(""));
return properties;
}
Entity:
#Entity
#Cache(usage=CacheConcurrencyStrategy.READ_ONLY,region="myLpn")
public class MyLpn {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="myLpnSeq")
#SequenceGenerator(name="myLpnSeq",sequenceName="MYLPN_SEQ")
private Long lpnId;
private String tcLpnId;
//I have all setter/gettters properly
Service Impl:
#Transactional
public MyLpn getLpnById(Long lpnId) {
MyLpn lpn = null;
try {
lpn = lpnDaoImpl.getLpnById(lpnId);
} catch (DataAccessException dataAccessException) {
System.out.println();
throw new DataAccessResourceFailureException("Exception Occured");
}
return lpn;
}
DAOImple:
public MyLpn getLpnById(Long lpnId) {
Session currentSession = sessionFactory.getCurrentSession();
Query query = currentSession.createQuery("FROM MyLpn myLpn WHERE lpnId = :lpnId");
query.setLockMode("myLpn", LockMode.PESSIMISTIC_WRITE);
query.setParameter("lpnId", lpnId);// "lpnId"
query.setCacheable(true);
List<?> returnedList = query.list();
return (MyLpn) returnedList.get(0);
}
But my doubt is
When i request an Entity I see SQL query in the app logs. This is fine, lets suppose that For the first time Entity has been fetched from DB and Stored in Second->First Level Cache.
But When I request for the same entity second time again I see the SQL in the app logs. This is making me confuse.
Can you please let me know why Hibernate is executing a SQL even i configured both second level and query cache.

Mock spring's LocalContainerEntityManagerFactoryBean method using Mockito?

I am trying to write unit testcases for the below code and am trying to mock the EntityManager implementation. I am unable to do so and I get null entity manager bean in my test class.
public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp)
{
List<Object[]> result = null;
EntityManager em = null;
try {
query = String.format(query, startTime, endTimestamp, siteId);
logger.debug(" Query : " + query);
em = localContainerEntityManagerFactoryBean.nativeEntityManagerFactory.createEntityManager();
EntityTransaction et = em.getTransaction();
et.begin();
result = (List<Object[]>) em.createNativeQuery(query).getResultList();
//logger.debug("Results from the query : " + query + " are :" + Utility.toJsonString(result, true));
} catch (Exception ex) {
ex.printStackTrace();
logger.error("Error Occurred while fetching the data for the query : " + query);
}
return result;
}
The test code I have written to mock it is below:
#InjectMocks
private LocalContainerEntityManagerFactoryBean emMock = new LocalContainerEntityManagerFactoryBean();
...
Mockito.when(localContainerEntityManagerFactoryBean.nativeEntityManagerFactory.createEntityManager()).thenReturn();
I should return a list when this is called as output So i need the whole method to be mocked. Please help !
First off all instead of #InjectMocks you should be using #Mock and put the #InjectMocks on the class you are trying to unit test.
However the fact that you are even considering mocking the LocalContainterEntityManagerFactoryBean is a sign that your code is flawed. You shouldn't be using the LCEMFB in code. It is only for configuration. It is a FactoryBean that creates an EntityManagerFactory so actually you should be injecting an EntityManagerFactory into your code which you should be mocking.
Instead of wiring the LCEMFB use the plain EMF and get an instance by annotating the field with #PersistenceUnit.
#PersistenceUnit
private EntityManagerFactory emf;
Then your method is also a bit cleaner
public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp)
{
List<Object[]> result = null;
EntityManager em = null;
try {
query = String.format(query, startTime, endTimestamp, siteId);
logger.debug(" Query : " + query);
em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();
et.begin();
result = (List<Object[]>) em.createNativeQuery(query).getResultList();
//logger.debug("Results from the query : " + query + " are :" + Utility.toJsonString(result, true));
} catch (Exception ex) {
ex.printStackTrace();
logger.error("Error Occurred while fetching the data for the query : " + query);
}
return result;
}
However what you actually should be doing is injecting an EntityManager and don't try to create one yourself (your code is still flawed as you aren't closing the transaction nor the created EntityManager which in turn will eventually lead you to being unable to connect to your database as the underlying Connection remains open as well.
So instead of injecting either the LCEMFB or a EMF use a plain EntityManager instead and let spring manage it for you. To have spring manage the transaction make sure there is an #EnableTransactionManagement or <tx:annotation-driven /> in your configuration else it won't work.
#PersistenceContext
private EntityManager em;
Now your method is really focussed on what it should do, get data from the database.
#Transactional(readOnly=true)
public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp) {
query = String.format(query, startTime, endTimestamp, siteId);
return em.createNativeQuery(query).getResultList();
}
Now in your test you should only need to mock the EntityManager.
All of this is also explained in the ORM chapter of the Spring Reference guide.
Another thing that worries me is that you are using a String and parsing that to be used as a query. This is potentially dangerous and a cause for SQL injection attacks. Instead of doing the formatting yourself you should let it be handled by Hibernate or JDBC.
#Transactional(readOnly=true)
public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp) {
query = String.format(query, startTime, endTimestamp, siteId);
Query q = em.createNativeQuery(query);
q.setParameter("siteId", siteId)
.setParameter("startTime", startTime)
.setParameter("endTime", endTimestamp);
return q.getResultList();
}
The code above assumes a query in the form of SELECT * FROM YOURTABLE WHERE siteId=:siteId and startTime >= :startTime and endTime <= :endTime (or whatever your SQL looks like).

Kundera cassandra - Transaction rollback and Entity update

I use Kundera-Cassandra 3.2 and want to use the transaction management from Kundera.
My handling looks like this:
EntityManager manager = repo.getEntityManagerFactory().createEntityManager(CassandraRepository.getProperties());
try{
manager.getTransaction().begin();
this.repo.update(account1, manager); //calls the merge method of the Entitymanager
this.repo.save(account2, manager); //calls the persist method of the Entitymanager
manager.getTransaction().commit();
} catch(Exception e){
if(manager.getTransaction().isActive()){
manager.getTransaction().rollback();
}
} finally {
manager.clear();
manager.close();
}
When an error in the this.repo.save(account2, manager); occurs, the manager rollbacks the transaction, but does not do a update statement, he makes a delete statement for the merge method. The reason for this is, when calling the merge methode, kundera creates an insert statement and not an update. But how to say Kundera to make an update to rollback the transaction also with an update.
Logs:
12:42:41.185 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000004'.
12:42:41.211 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000005'.
EDIT (my repository):
public class CassandraRepository<T> {
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public static Map<String, String> getProperties() {
final Map<String, String> properties = new HashMap<String, String>();
properties.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);
return properties;
}
public void update(T entity, EntityManager manager) throws Exception{
try {
manager.merge(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public void save(T entity, EntityManager manager) throws Exception{
try {
manager.persist(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
According to JPA, to update an entity you have to first bring it into managed state (by fetching it)
Example:-
PersonCassandra p = entityManager.find(PersonCassandra.class, "2");
entityManager.getTransaction().begin();
p.setMonth(Month.JAN);
entityManager.merge(p);
entityManager.persist(p3);
entityManager.getTransaction().commit();
Issue is not with INSERT and UPDATE statements since both are similar for Cassandra, under the hood.

java.sql.SQLException: This function is not supported using HSQL and Spring

Can someone please tell me why am I geting java.sql.SQLException: This function is not supported using HSQL and Spring? I am trying to insert a new row into my database..
Below is my DAO and I get the error on the mySession.save(message) line:
#Transactional
#Repository
public class MessageDaoImpl implements MessageDao
{
private Log log = null;
#Autowired
private SessionFactory sessionFactory;
public MessageDaoImpl()
{
super();
log = LogFactory.getLog(MessageDaoImpl.class);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public List<Message> listMessages()
{
try
{
return (List<Message>) sessionFactory.getCurrentSession()
.createCriteria(Message.class).list();
} catch (Exception e)
{
log.fatal(e.getMessage());
return null;
}
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void SaveOrUpdateMessage(Message message)
{
try
{
Session mySession = sessionFactory.getCurrentSession();
mySession.save(message);
mySession.flush();
} catch (Exception e)
{
log.fatal(e.getMessage());
}
}
}
Here is my main class:
public static void main(String[] args)
{
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
MessageService mService = context.getBean(MessageService.class);
HelloWorld helloWorld = context.getBean(HelloWorld.class);
/**
* Date: 4/26/13 / 9:26 AM
* Comments:
*
* I added Log4J to the example.
*/
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
Message message = new Message();
message.setMessage(helloWorld.getMessage());
//
mService.SaveMessage(message);
helloWorld.setMessage("I am in Staten Island, New York");
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
}
}
Here is my DatabaseConfig:
public class DatabaseConfig
{
private static final Logger LOGGER = getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).
addScript("schema.sql").build();
return db;
}
#Bean
public DataSource hsqlDataSource() {
BasicDataSource ds = new BasicDataSource();
try {
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUsername("sa");
ds.setPassword("");
ds.setUrl("jdbc:hsqldb:mem:mydb");
}
catch (Exception e)
{
LOGGER.error(e.getMessage());
}
return ds;
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(hsqlDataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[]{"com.xxxxx.HelloSpringJavaBasedJavaConfig.model"});
try
{
factoryBean.afterPropertiesSet();
} catch (IOException e)
{
LOGGER.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
}
and here is what I am getting to the console:
Exception in thread "main" org.hibernate.AssertionFailure: null id in com.xxx.HelloSpringJavaBasedJavaConfig.model.Message entry (don't flush the Session after an exception occurs)
Here is my message model bean:
#Entity
#Table(name = "messages")
public class Message
{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
#Column(name = "message")
private String message;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
#Override
public String toString()
{
return "Message{" +
"id='" + id + '\'' +
", message='" + message + '\'' +
'}';
}
}
This relates to the version of hsql being used probably the version causing issue was 1.8 with hibernate 4. Need to use 2.2.9 instead
You can't use a String with #GenerateValue with the Strategy GenerationType.AUTO since it uses sequence generator and those can't be used with non-numerical values. You have to set it yourself. Use an Integer or Long if you want it to be generated for you.
Hibernate docs
Or use an id generator that uses string values
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
it was a version issues. I updated the versions and now everything works
I had the same issue after I upgraded hibernate to version 4.2.8 .Looking in the logs, I noticed that the sql query generated by hibernate tried to insert a record with a null primary key. The field was annotated just with: #Id #GeneratedValue
Upgrading hsqldb to version 2.2.9 made this disappear just like Denis said and I am very thankful to him for the reply.
It seems very likely that this issue is related to attempting to use a Session which has already signaled an error. As Sotirios mentioned, it is a bad idea to catch exceptions in your DAO, and if you do, it is critical that you re-throw them.
Normally, once you catch a Hibernate exception, you must roll back your transaction and close the session as the session state may no longer be valid (Hibernate core documentation).
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
Since you're using Spring, most of that doesn't apply to you, but the exception message you are seeing indicates that the actual cause of your problem probably was related to catching an exception and then continuing to use the same session.

Categories