How to implement ALTER TABLE Query using Spring Data Jpa or Hibernate - spring

Im inserting a CSV file in a database table using Spring Batch and Spring Data (and Hibernate).
each time I insert the CSV I have to delete the previous data in the table using the data-jpa deleteAll() method. the problem is that the ids of the table are incremented automatically and continuously (#GeneratedValue(strategy = GenerationType.IDENTITY)) after each delete/insert statement.
I want that after each delete the ids start on 1. the only way that I found to do that is by altering the index (i know its not the best way, so your suggestions are welcomed)
the Question is :
is there any method to run this SQL request
ALTER TABLE res AUTO_INCREMENT=1;
in Java object using Spring Data or Hibernate?
Thanks

Is it possible to generate id on java side and do not use embedded db autoincrement feature?
So the best way will be to generate id explicitly and set it to entity.
Other cases are:
Truncate Table
TRUNCATE TABLE table_name;
This will reset the auto increment on the table as well as deleting all records from that table.
Drop and Recreate
Table DROP TABLE table_name;
CREATE TABLE table_name { ... };
So I think, second is what are you looking for

Instead of Altering the table, I have customized the way that Hibernate Generates the Ids.
instead of using :
#GeneratedValue(strategy = GenerationType.IDENTITY)
I have implemented a custom id generator :
#GenericGenerator(name = "sequence_id", strategy =
"com.xyz.utils.CustomIdGenerator",
parameters = {
#org.hibernate.annotations.Parameter(
name = "table_name", value = "myTable")
})
#GeneratedValue(generator = "sequence_id")
the CustomIdGenerator class :
public class CustomIdGenerator implements IdentifierGenerator,Configurable{
private String table_name ;
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object)
throws HibernateException {
Connection connection = session.connection();
try {
Statement statement=connection.createStatement();
ResultSet rs=statement.executeQuery("select count(id) as Id from "
+table_name );
if(rs.next())
{
int id=rs.getInt(1)+1;
Integer generatedId = new Integer(id);
System.out.println("Generated Id: " + generatedId);
return generatedId;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry)
throws MappingException {
setTable_name(params.getProperty("table_name")); }
//getters and setters
}
the problem of this solution is the execution of select for each id so it seems generating load on the DBMS and its slow.
and the rs is looping twice for the first id ()
any suggestion for optimization is welcomed

Related

How to access PostgreSQL RETURNING value in Spring Boot DAO?

I want to return the auto-generated id of entity. PostgeSQL is able to automaticaly selects certain column via RETURNING, but I have a hard time trying to find how to retrieve this value in Spring Boot.
I would want something like:
public int createUser(User user) {
String sql = "INSERT INTO user (name, surname) VALUES (?,?) RETURNING id";
return jdbcTemplate.update(sql,
user.getName(),
user.getSurname(),
resultSet -> resultSet.getInt("id")
);
}
I know it's straightforward in Hibernate, then whether you use a Repository class or an EntityManager, the save method returns the saved entity, so you can just do:
int id = userRepository.save(user).getId();
Or is there a reason you want to persist it the way you do?

Spring transaction not getting rolled back

I am using spring transactions and hibernate to insert data into the oracle database table:
Here is the scenarion I am facing problem with:
I have two tables which have one to one mapping in hibernate. And I am inserting data in these two tables using below method calls.Transaction propagates from one method to another.So that insertion of data in both the tables happens in one transaction.
Problem: is that , while inserting data in second table, if an exception like "constraintvoilationexception---can not insert null into a particular column", is thrown,....then ideally the data should not be inserted in any of the tables i.e transaction should roll back,,,...But this is not happening ...when an exception is thrown while inserting data in the second table....records do get inserted in the first table, which ideally should not happen i.e whole transaction should be rolled back...
Can you please help,...where I am wrong while applying #Transactional , or there is some other reason for this ( may be from database side, not sure though)
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void methodA(){
// inserting data in table 1;
methodB();
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void methodB{
// inserting data in table 2;
}
Defines zero (0) or more exception classes, which must be subclasses of Throwable, indicating which exception types must cause a transaction rollback. Details Here
#Transactional(readOnly = false, propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void methodA(){
try{
// inserting data in table 1;
methodB();
}
catch(Exception ex)
{
}
}
public void methodB{
// inserting data in table 2;
}

Spring Data JPA + Oracle Trigger increments the ID twice

I use the following tech stack:
spring-boot-starter-data-jpa
HikariCP for connection pooling
Oracle DB
My actual code looks similar to this.
/// My trigger looks like this
CREATE OR REPLACE TRIGGER FILE_BRI
BEFORE INSERT
ON FILE
FOR EACH ROW
BEGIN
SELECT FILE_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END;
///
public class FILE implements Serializable {
#Id
#SequenceGenerator(
name = "FILE_SEQ",
sequenceName = "FILE_SEQ",
allocationSize = 1)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "FILE_SEQ"
)
private long id;
}
public class ServiceA () {
#Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED)
public File insertFile() {
// Below line returns the inserted File object with ID as '58496'
return fileRepository.save(file)
}
#Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED)
public AccessControl insertAccessControl() {
// Below line results in 'SQLIntegrityConstraintViolationException' (full error at the bottom of this post)
return accessControlRepository.save(accessControlFile)
}
}
Public class FileProcessor() {
ServiceA serviceA;
public void someMethod() {
// insert the file and get the inserted record
File insertedFile = serviceA.insertFile(file);
// get the ID from the inserted file and make another insert into another table
serviceA.insertAccessControl(insertedFile.getId()); // inserted file ID is '58496'
}
}
This is my investigation:
When I verified the ID of the inserted record in the table "FILE" is '58497', however repository.save() returned a different value.
When I make the second insert on table "ACCESS_CONTROL_FILE" with FILE_ID as '58496' it results in the error below because the FILE with ID as '58496' does not exist.
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("DB_OWNER"."ACCESS_CONTROL_FILE"."FILE_ID")
I'm puzzled as to why would repository.save() return a different ID(i.e. ID=58496) than what is actually inserted(ID=58497) in the database!
I've investigated all options that I could find on the internet related to 'Propagation and Isolation'.
As mentioned in comments, Looks like a database trigger is causing the issue. Disable the trigger to let JPA to manage the ID generation.

Native SQL from Spring / Hibernate without entity mapping?

I need to write some temporary code in my existing Spring Boot 1.2.5 application that will do some complex SQL queries. By complex, I mean a single queries about 4 different tables and I have a number of these. We all decided to use existing SQL to reduce potential risk of getting the new queries wrong, which in this case is a good way to go.
My application uses JPA / Hibernate and maps some entities to tables. From my research it seems like I would have to do a lot of entity mapping.
I tried writing a class that would just get the Hibernate session object and execute a native query but when it tried to configure the session factory it threw an exception complaining it could not find the config file.
Could I perhaps do this from one of my existing entities, or at least find a way to get the Hibernate session that already exists?
UPDATE:
Here is the exception, which makes perfect sense since there is no config file to find. Its app configured in the properties file.
org.hibernate.HibernateException: /hibernate.cfg.xml not found
at org.hibernate.internal.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:173)
For what it's worth, the code:
#NamedNativeQuery(name = "verifyEa", query = "select account_nm from per_person where account_nm = :accountName")
public class VerifyEaResult
{
private SessionFactory sessionFact = null;
String accountName;
private void initSessionFactory()
{
Configuration config = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).getBootstrapServiceRegistry();
sessionFact = config.buildSessionFactory(serviceRegistry);
}
public String getAccountName()
{
// Quick simple test query
String sql = "SELECT * FROM PER_ACCOUNT WHERE ACCOUNT_NM = 'lynnedelete'";
initSessionFactory();
Session session = sessionFact.getCurrentSession();
SQLQuery q = session.createSQLQuery(sql);
List<Object> result = q.list();
return accountName;
}
}
You can use Data access with JDBC, for example:
public class Client {
private final JdbcTemplate jdbcTemplate;
// Quick simple test query
final static String SQL = "SELECT * FROM PER_ACCOUNT WHERE ACCOUNT_NM = ?";
#Autowired
public Client(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getData(String name) {
return jdbcTemplate.queryForList(SQL, name);
}
}
The short way is:
jdbcTemplate.queryForList("SELECT 1", Collections.emptyMap());

what is difference between ResultSetExtractor vs Rowmapper?

I worked on both row mapper and resultset extractor call back interfaces.I found difference i.e.,
1.Row mapper can be processing per row basis.But Resultset extractor we can naviagte all rows and return type is object.
Is there any difference other than above?.How the works Rowmapper internal and return type is list?.
Basic difference is with ResultsetExtractor you will need to iterate through the result set yourself, say in while loop.
This interface provides you processing of the entire ResultSet at once. The implemetation of Interface method extractData(ResultSet rs) will contain that manual iteration code.
See one implementation of ResultsetExtractor
while some callback handlers like RowCallbackHandler, the interface method processRow(ResultSet rs) loops for you.
RowMapper can be used both was for mapping each row, or entire rows.
For entire rows Object (by template method jdbcTemplate.query())
public List findAll() {
String sql = "SELECT * FROM EMPLOYEE";
return jdbcTemplate.query(sql, new EmployeeRowMapper());
}
without casting will work
For individual object (with Template method jdbcTemplate.queryForObject())
#SuppressWarnings({ "unchecked", "rawtypes" })
public Employee findById(int id) {
String sql = "SELECT * FROM EMPLOYEE WHERE ID = ?";
// jdbcTemplate = new JdbcTemplate(dataSource);
Employee employee = (Employee) jdbcTemplate.queryForObject(sql, new EmployeeRowMapper(), id );
// Method 2 very easy
// Employee employee = (Employee) jdbcTemplate.queryForObject(sql, new Object[] { id }, new BeanPropertyRowMapper(Employee.class));
return employee;
}
#SuppressWarnings("rawtypes")
public class EmployeeRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getInt("ID"));
employee.setName(rs.getString("NAME"));
employee.setAge(rs.getInt("AGE"));
return employee;
}
}
Best Use cases:
Row Mapper: When each row of a ResultSet maps to a domain Object, can be implemented as private inner class.
RowCallbackHandler: When no value is being returned from callback method for each row, e.g. writing row to a file, converting rows to a XML, Filtering rows before adding to collection. Very efficient as ResultSet to Object mapping is not done here.
ResultSetExtractor: When multiple rows of ResultSet map to a single Object. Like when doing complex joins in a query one may need to have access to entire ResultSet instead of single row of rs to build complex Object and you want to take full control of ResultSet. Like Mapping the rows returned from the join of TABLE1 and TABLE2 to an fully-reconstituted TABLE aggregate.
ParameterizedRowMapper is used to create complex objects
JavaDoc of ResultSetExtractor:
This interface is mainly used within the JDBC framework itself. A RowMapper is usually a simpler choice for ResultSet processing, mapping one result object per row instead of one result object for the entire ResultSet.
ResultSetExtractor is suppose to extract the whole ResultSet (possibly multiple rows), while RowMapper is feeded with row at a time.
Most the time, ResultSetExtractor will loop the ResultSet and use RowMapper, snippet example of Spring RowMapperResultSetExtractor:
List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
int rowNum = 0;
while (rs.next()) {
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
Pay attention, ALL results will be transformed, this can create Out Of Memory exception.
See also
RowMapperResultSetExtractor
RowMapper: To process one record of ResultSet at a time.
ResultSetExtractor: To process multiple records of ResultSet at a time.
I think one place where a ResultSetExtractor could be advantageous is when you have a result set (like from a call to a stored procedure) and a row mapper, and want to process them like is done under the covers in the jdbcTemplate methods, such as query(String sql, RowMapper rowMapper). In this case you can save yourself from having to manually iterate over the result set by using the ResultSetExtractor instead of just the RowMapper.
For example:
RowMapper
ResultSet resultSet = cs.executeQuery();
int row = 0;
DateRowMapper dateRowMapper = new DateRowMapper();
List<String> dates = new ArrayList<>();
while (resultSet.next()) {
dates.add(dateRowMapper.mapRow(resultSet, ++row));
}
return dates;
ResultSetExtractor
ResultSet resultSet = callableStatement.executeQuery();
return new RowMapperResultSetExtractor<>(new DateRowMapper()).extractData(resultSet);

Resources