Spring Transactions failing in multiple instance applications - oracle

Spring transactions in multiple instance application is failing regularly. Unable to reproduce. Failing with PK constraint(jobname+key)
Note: I have modified the query part to look-alike
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
public boolean insertLockIfDoesNotExist(String jobName, String key) {
try {
int affectedRowsCount = entityManager.createNativeQuery(
"insert into LOCK_SCH (JOB_NAME, KEY) select ?, ? where not exists (select 1 from LOCK_SCH where JOB_NAME=? and EXECUTION_KEY=?) ")
.setParameter("jobName", jobName)
.setParameter("key", key)
.setFlushMode(FlushModeType.AUTO)
.executeUpdate();
return affectedRowsCount > 0;
} catch (Exception e) {
throw e;
}
}

Related

SpringBoot+JDBCTemplate+Parent Key not found error

Unable to insert into child table in a single transaction using springboot and jdbctemplate.
Controller:
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public Response addUser(#RequestBody UserVo userVo) throws Exception
{
service.addUserRecord(userVo);
}
Service Layer:
#Autowired
private Dao dao;
#Transactional(readOnly = false)
public void addUserRecord(#RequestBody UserVo userVo) throws Exception
{
int parentSeqNo = dao.addUser();
int result = dao.addUserDetails(parentSeqNo, list);
}
DAOImple class:
#Autowired
private JdbcTemplate jdbcTemplate;
public int addUser()
{
try
{
String sql = "INSERT INTO user_table() VALUES (?,?,?,?,?) ";
jdbcTemplate.update(sql,....);
return seqNo;
}
catch(Exception e)
{
e.printStackTrace();
throw e;
}
}
public void addUserDetails(String seqNo, List<String> list){
String sql="insert into user_details_table values(?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
String img = list.get(i);
ps.setString(1, seqNo);
ps.setString(2, img);
}
public int getBatchSize() {
return list.size();
}
});
return;
}
Issue/Error:
ORA-02291: integrity constraint (FK3) violated - parent key not found; nested exception is java.sql.SQLIntegrityConstraintViolationException:
Database tables and constraints are looks good and tried to insert using sql editor, just working fine. While testing from postman tool, getting parent key not found exception. Greatly appreciated for any suggestions on this issue.

transactional unit testing with ObjectifyService - no rollback happening

We are trying to use google cloud datastore in our project and trying to use objectify as the ORM since google recommends it. I have carefully used and tried everything i could read about and think of but somehow the transactions don't seem to work. Following is my code and setup.
#RunWith(SpringRunner.class)
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ContextConfiguration(classes = { CoreTestConfiguration.class })
public class TestObjectifyTransactionAspect {
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
// Our tests assume strong consistency
new LocalDatastoreServiceTestConfig().setApplyAllHighRepJobPolicy(),
new LocalMemcacheServiceTestConfig(), new LocalTaskQueueTestConfig());
private Closeable closeableSession;
#Autowired
private DummyService dummyService;
#BeforeClass
public static void setUpBeforeClass() {
// Reset the Factory so that all translators work properly.
ObjectifyService.setFactory(new ObjectifyFactory());
}
/**
* #throws java.lang.Exception
*/
#Before
public void setUp() throws Exception {
System.setProperty("DATASTORE_EMULATOR_HOST", "localhost:8081");
ObjectifyService.register(UserEntity.class);
this.closeableSession = ObjectifyService.begin();
this.helper.setUp();
}
/**
* #throws java.lang.Exception
*/
#After
public void tearDown() throws Exception {
AsyncCacheFilter.complete();
this.closeableSession.close();
this.helper.tearDown();
}
#Test
public void testTransactionMutationRollback() {
// save initial list of users
List<UserEntity> users = new ArrayList<UserEntity>();
for (int i = 1; i <= 10; i++) {
UserEntity user = new UserEntity();
user.setAge(i);
user.setUsername("username_" + i);
users.add(user);
}
ObjectifyService.ofy().save().entities(users).now();
try {
dummyService.mutateDataWithException("username_1", 6L);
} catch (Exception e) {
e.printStackTrace();
}
List<UserEntity> users2 = this.dummyService.findAllUsers();
Assert.assertEquals("Size mismatch on rollback", users2.size(), 10);
boolean foundUserIdSix = false;
for (UserEntity userEntity : users2) {
if (userEntity.getUserId() == 1) {
Assert.assertEquals("Username update failed in transactional context rollback.", "username_1",
userEntity.getUsername());
}
if (userEntity.getUserId() == 6) {
foundUserIdSix = true;
}
}
if (!foundUserIdSix) {
Assert.fail("Deleted user with userId 6 but it is not rolledback.");
}
}
}
Since I am using spring, idea is to use an aspect with a custom annotation to weave objectify.transact around the spring service beans methods that are calling my daos.
But somehow the update due to ObjectifyService.ofy().save().entities(users).now(); is not gettign rollbacked though the exception throws causes Objectify to run its rollback code. I tried printing the ObjectifyImpl instance hashcodes and they are all same but still its not rollbacking.
Can someone help me understand what am i doing wrong? Havent tried the actual web based setup yet...if it cant pass transnational test cases there is no point in actual transaction usage in a web request scenario.
Update: Adding aspect, services, dao as well to make a complete picture. The code uses spring boot.
DAO class. Note i am not using any transactions here because as per code of com.googlecode.objectify.impl.TransactorNo.transactOnce(ObjectifyImpl<O>, Work<R>) a transnational ObjectifyImpl is flushed and committed in this method which i don't want. I want commit to happen once and rest all to join in on that transaction. Basically this is the wrong code in com.googlecode.objectify.impl.TransactorNo ..... i will try to explain my understanding a later in the question.
#Component
public class DummyDaoImpl implements DummyDao {
#Override
public List<UserEntity> loadAll() {
Query<UserEntity> query = ObjectifyService.ofy().transactionless().load().type(UserEntity.class);
return query.list();
}
#Override
public List<UserEntity> findByUserId(Long userId) {
Query<UserEntity> query = ObjectifyService.ofy().transactionless().load().type(UserEntity.class);
//query = query.filterKey(Key.create(UserEntity.class, userId));
return query.list();
}
#Override
public List<UserEntity> findByUsername(String username) {
return ObjectifyService.ofy().transactionless().load().type(UserEntity.class).filter("username", username).list();
}
#Override
public void update(UserEntity userEntity) {
ObjectifyService.ofy().save().entity(userEntity);
}
#Override
public void update(Iterable<UserEntity> userEntities) {
ObjectifyService.ofy().save().entities(userEntities);
}
#Override
public void delete(Long userId) {
ObjectifyService.ofy().delete().key(Key.create(UserEntity.class, userId));
}
}
Below is the Service class
#Service
public class DummyServiceImpl implements DummyService {
private static final Logger LOGGER = LoggerFactory.getLogger(DummyServiceImpl.class);
#Autowired
private DummyDao dummyDao;
public void saveDummydata() {
List<UserEntity> users = new ArrayList<UserEntity>();
for (int i = 1; i <= 10; i++) {
UserEntity user = new UserEntity();
user.setAge(i);
user.setUsername("username_" + i);
users.add(user);
}
this.dummyDao.update(users);
}
/* (non-Javadoc)
* #see com.bbb.core.objectify.test.services.DummyService#mutateDataWithException(java.lang.String, java.lang.Long)
*/
#Override
#ObjectifyTransactional
public void mutateDataWithException(String usernameToMutate, Long userIdToDelete) throws Exception {
//update one
LOGGER.info("Attempting to update UserEntity with username={}", "username_1");
List<UserEntity> mutatedUsersList = new ArrayList<UserEntity>();
List<UserEntity> users = dummyDao.findByUsername(usernameToMutate);
for (UserEntity userEntity : users) {
userEntity.setUsername(userEntity.getUsername() + "_updated");
mutatedUsersList.add(userEntity);
}
dummyDao.update(mutatedUsersList);
//delete another
UserEntity user = dummyDao.findByUserId(userIdToDelete).get(0);
LOGGER.info("Attempting to delete UserEntity with userId={}", user.getUserId());
dummyDao.delete(user.getUserId());
throw new RuntimeException("Dummy Exception");
}
/* (non-Javadoc)
* #see com.bbb.core.objectify.test.services.DummyService#findAllUsers()
*/
#Override
public List<UserEntity> findAllUsers() {
return dummyDao.loadAll();
}
Aspect which wraps the method annoted with ObjectifyTransactional as a transact work.
#Aspect
#Component
public class ObjectifyTransactionAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(ObjectifyTransactionAspect.class);
#Around(value = "execution(* *(..)) && #annotation(objectifyTransactional)")
public Object objectifyTransactAdvise(final ProceedingJoinPoint pjp, ObjectifyTransactional objectifyTransactional) throws Throwable {
try {
Object result = null;
Work<Object> work = new Work<Object>() {
#Override
public Object run() {
try {
return pjp.proceed();
} catch (Throwable throwable) {
throw new ObjectifyTransactionExceptionWrapper(throwable);
}
}
};
switch (objectifyTransactional.propagation()) {
case REQUIRES_NEW:
int limitTries = objectifyTransactional.limitTries();
if(limitTries <= 0) {
Exception illegalStateException = new IllegalStateException("limitTries must be more than 0.");
throw new ObjectifyTransactionExceptionWrapper(illegalStateException);
} else {
if(limitTries == Integer.MAX_VALUE) {
result = ObjectifyService.ofy().transactNew(work);
} else {
result = ObjectifyService.ofy().transactNew(limitTries, work);
}
}
break;
case NOT_SUPPORTED :
case NEVER :
case MANDATORY :
result = ObjectifyService.ofy().execute(objectifyTransactional.propagation(), work);
break;
case REQUIRED :
case SUPPORTS :
ObjectifyService.ofy().transact(work);
break;
default:
break;
}
return result;
} catch (ObjectifyTransactionExceptionWrapper e) {
String packageName = pjp.getSignature().getDeclaringTypeName();
String methodName = pjp.getSignature().getName();
LOGGER.error("An exception occured while executing [{}.{}] in a transactional context."
, packageName, methodName, e);
throw e.getCause();
} catch (Throwable ex) {
String packageName = pjp.getSignature().getDeclaringTypeName();
String methodName = pjp.getSignature().getName();
String fullyQualifiedmethodName = packageName + "." + methodName;
throw new RuntimeException("Unexpected exception while executing ["
+ fullyQualifiedmethodName + "] in a transactional context.", ex);
}
}
}
Now the problem code part that i see is as follows in com.googlecode.objectify.impl.TransactorNo:
#Override
public <R> R transact(ObjectifyImpl<O> parent, Work<R> work) {
return this.transactNew(parent, Integer.MAX_VALUE, work);
}
#Override
public <R> R transactNew(ObjectifyImpl<O> parent, int limitTries, Work<R> work) {
Preconditions.checkArgument(limitTries >= 1);
while (true) {
try {
return transactOnce(parent, work);
} catch (ConcurrentModificationException ex) {
if (--limitTries > 0) {
if (log.isLoggable(Level.WARNING))
log.warning("Optimistic concurrency failure for " + work + " (retrying): " + ex);
if (log.isLoggable(Level.FINEST))
log.log(Level.FINEST, "Details of optimistic concurrency failure", ex);
} else {
throw ex;
}
}
}
}
private <R> R transactOnce(ObjectifyImpl<O> parent, Work<R> work) {
ObjectifyImpl<O> txnOfy = startTransaction(parent);
ObjectifyService.push(txnOfy);
boolean committedSuccessfully = false;
try {
R result = work.run();
txnOfy.flush();
txnOfy.getTransaction().commit();
committedSuccessfully = true;
return result;
}
finally
{
if (txnOfy.getTransaction().isActive()) {
try {
txnOfy.getTransaction().rollback();
} catch (RuntimeException ex) {
log.log(Level.SEVERE, "Rollback failed, suppressing error", ex);
}
}
ObjectifyService.pop();
if (committedSuccessfully) {
txnOfy.getTransaction().runCommitListeners();
}
}
}
transactOnce is by code / design always using a single transaction to do things. It will either commit or rollback the transaction. there is no provision to chain transactions like a normal enterprise app would want.... service -> calls multiple dao methods in a single transaction and commits or rollbacks depending on how things look.
keeping this in mind, i removed all annotations and transact method calls in my dao methods so that they don't start an explicit transaction and the aspect in service wraps the service method in transact and ultimately in transactOnce...so basically the service method is running in a transaction and no new transaction is getting fired again. This is a very basic scenario, in actual production apps services can call other service methods and they might have the annotation on them and we could still end up in a chained transaction..but anyway...that is a different problem to solve....
I know NoSQLs dont support write consistency at table or inter table levels so am I asking too much from google cloud datastore?

Getting ambiguous result using JDBC Metadata API for Hive

I am trying to get Table names for hive using DatabaseMetaData in a similar way like RDBMS.
Sample code:
try (Connection con = getJdbcConnection(connectionUri, driverName, username, password);) {
DatabaseMetaData metadata = con.getMetaData();
ResultSet rs = metadata.getTables(null, null, tableName, null);
while (rs.next()) {
System.out.println(rs.getString(3));
}
} catch (SQLException e) {
}
private static void registerDriver(String driverName) {
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
LOG.error("No class found for " + driverName + ". Details: " + e);
}
}
private static Connection getJdbcConnection(String connectionUri, String driverName, String username,
String password) throws SQLException{
registerDriver(driverName);
return DriverManager.getConnection(connectionUri, username,password);
}
There is no table in a particular database. Using different different table names, I am getting different output.
For example:
I put table name emp, there are 3 records with name emp
I put table name employee, there are 5 records with name employee
I put table name emp12, it is returning no records (which is expected)
Am I doing something wrong?
Shouldn't I use DatabaseMetaData for checking table existence?
I need to pass schema name in getTables method
Signature:
ResultSet getTables(String catalog,
String schemaPattern,
String tableNamePattern,
String[] types)
throws SQLException
I passed following agruments:
catalog = null;
schemaPattern = Hive schema Name
tableNamePattern = Hive Table Name
types = new String[] { "TABLE" }
Sample code:
try (Connection con = getJdbcConnection(connectionUri, driverName, username, password);) {
DatabaseMetaData metadata = con.getMetaData();
ResultSet rs = metadata.getTables(null, schemaName, tableName, new String[] { "TABLE" });
while (rs.next()) {
String tName = rs.getString("TABLE_NAME");
if (tName != null && tName.equals(tableName)) {
LOG.info("Table [" + tableName + "] is present in the Database.");
return true;
}
}
rs.close();
LOG.info("Table [" + tableName + "] is not present in the Database.");
return false;
} catch (SQLException e) {
LOG.error("Not able to get Table Metadata . Caused By: " + e);
}

What is happening internally for auto increment field in data base side and application side?

I have a bean for spring application
#Entity
#Table(name="tbl_apply_leave")
public class ApplyLeaveModel{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int dbid;
private String employee_name;
public int getDbid() {
return dbid;
}
public void setDbid(int dbid) {
this.dbid = dbid;
}
public String getEmployee_name() {
return employee_name;
}
public void setEmployee_name(String employee_name) {
this.employee_name = employee_name;
}
}
then In controller before save this object value of dbid is null.
but after save the object using hibernate dbid contains value.
I don't know what's happening.
controller code is:
System.out.println(applyLeaveModel.getDbid()); // null
leave_dao.saveApplyLeaveModel(applyLeaveModel);
System.out.println(applyLeaveModel.getDbid()); // 5
public void saveApplyLeaveModel(ApplyLeaveModel applyLeaveModel) {
Session session = null;
Transaction trans_obj = null;
try {
session = sessionFactory.openSession();
if (session.isOpen() && session != null) {
trans_obj = session.beginTransaction();
session.persist(applyLeaveModel);
}
} catch (Exception e) {
System.out.println("save ApplyLeaveModel session " + e);
} finally {
trans_obj.commit();
session.close();
}
}
Hibernate executes PreparedStatement.getGeneratedKeys() to obtain the generated id after insert.
Also related: hibernate.jdbc.use_get_generated_keys.
Sidenote: Primitive types in Java cannot have null value. Probably you mean 0, or type of the id is Integer.

Is it a good programming to pass Connection Object to a method?

I am doing a Insert Operation , i have a condition if company is 0 , then i need to perform an additional insert in another table ??
This is my code
public static String insertIntoDepotTable(DepotJSONBean depotbean) throws SQLException
{
Connection dbConnection = null;
PreparedStatement depotjsoninsertPst = null ;
try
{
dbConnection = DBConnectionOrientDepot.getDBConnection();
dbConnection.setAutoCommit(false);
String companyId = depotbean.getCompanyId();
if(companyId.equals("0"))
{
saveInCompany(depotbean , dbConnection);
}
String Insertsql = "INSERT INTO tbl_depot values (depotID,depoBelongsToID,stateID,districtID,talukMandalID,depotName,companyID,contactName,phone1,phone2,address,latitude,longititude,accuracy,town,noOfPeopleOperating,depotSize,storageCapacity,cAndFNames,depotPic1,depotPic2,comments,active,createdOn,modifiedOn) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
depotjsoninsertPst = dbConnection.prepareStatement(Insertsql);
}
catch(Exception e)
{
}
} // end of method
public String saveInCompany(DepotJSONBean djsonbean , Connection conn)
{
}

Resources