How to set DB2 global variable on session from spring data jpa context - jpa

I have a spring data jpa repository. I want to query a database view.
The database is DB2 and supports a feature called 'global variable' which actually is a session variable. The db view declaration uses a global variable.
How do I set the value of this variable at runtime before the view is executed?
Would something like this work?
public interface DomainRepository extends org.springframework.data.jpa.repository.JpaRepository<Domain, IdType> {
#Query(value = "SET SCHEMA.VAR_GLOBAL = :param; SELECT * FROM SCHEMA.DOMAIN", nativeQuery = true)
List<Domain> findByDomain(#Param("param") String param);
}
Are there any alternative solutions?

That's what I want to achieve in SQL:
SET CURRENT APPLICATION COMPATIBILITY = 'V11R1';
SET SCHEMA.VAR_GL = 'Value';
SELECT * FROM SCHEMA.VIEW;
The SCHEMA.VIEW is declared as:
SELECT * FROM SCHEMA.TABLE WHERE field = VAR_GL
I have to mark the public method on the controller with #Transactional annotation to execute the queries together in one db session.
public interface Controller {
#Transactional
List<Options> loadOptions();
}
public class ControllerImpl implements Controller {
#Autowired
private DomainRepository repo;
#Override
public List<Option> loadOptions() {
this.repo.setCompatibilityMode();
this.repo.setGlobalVariableA("Value");
List<Option> list = this.repo.loadDropdown();
return list;
}
In my jpa repository I need a method for each global variable:
public interface DomainRepository extends JpaCrudRepository<Option, OptionPK> {
#Modifying
#Query(value = "SET CURRENT APPLICATION COMPATIBILITY = 'V11R1'", nativeQuery = true)
void setCompatibilityMode();
}
#Modifying
#Query(value = "set SCHEMA.VAR_GL = :value", nativeQuery = true)
void setGlobalVariableA(#Param("value") String value);
#Query(value = "SELECT * FROM SCHEMA.VIEW", nativeQuery = true)
List<Option> loadDropdown();
}
That's how it works. I'd like to improve this solution further but I have no idea how to make the variable query more reusable by making the variable name a parameter.

Variable declarations don't work over JDBC but temp tables do:
DECLARE GLOBAL TEMPORARY TABLE SESSION.myvars
(
VAR_GLOBAL VARCHAR(255)
) ON COMMIT PRESERVE ROWS;

You cannot lump two statements together like in your example, but you should be able to create two different Querys and execute them in separate calls to executeUpdate() and getResultList() respectively. (I'm not very familiar with JPA, I may have used wrong method names, hopefully you get the idea.)

You can try CRUD methods:
public interface DomainRepository extends CrudRepository<Domain, Long>{
List<Domain> findAll();
}

Related

How to override save method in jdbc repository with insert sql statement

I'm using Micronaut Data JDBC (not JPA). It works fine with queries, where I can extend the CrudRepositry interface and add the queries as needed. For example:
#JdbcRepository
public interface MyRespository extends CrudRepository<MyEntity, String> {
#Query(
value = "...",
countQuery = "..."
)
Page<String> findByNameEquals(String name, Pageable pageable);
}
All seems fine until I want to override the save(MyEntity myEntity) method. I'm expecting something like this:
#Insert(
value = "insert into my_entity (id, name) values (uuid_to_bin(uuid()), :myEntity.name"
)
public MyEntity(MyEntity myEntity)
But it doesn't seem to exist.
Does it mean I have to write another class for this save method, and as a result will end up with two repository classes, one for queries and one for overriding the save method? If yes how do I get a database connection object with which I can create and execute my sql statement?
Thanks!
-Fujian

How to find top N elements in Spring Data Jpa?

In Spring Data Jpa to get first 10 rows I can do this findTop10By...(). In my case the number or rows is not defined and comes as a parameter.
Is there something like findTopNBy...(int countOfRowsToGet)?
Here is another way without native query. I added Pageable as a parameter to the method in the interface.
findAllBySomeField(..., Pageable pageable)
I call it like this:
findAllBySomeField(..., PageRequest.of(0, limit)) // get first N rows
findAllBySomeField(..., Pageable.unpaged()) // get all rows
I don't know of a way to do exactly what you want, but if you are open to using #Query in your JPA repository class, then a prepared statement is one alternative:
#Query("SELECT * FROM Entity e ORDER BY e.id LIMIT :limit", nativeQuery=true)
Entity getEntitiesByLimit(#Param("limit") int limit);
Did it by using pagination, as described in the first answer. Just adding a more explicit example.
This example will give you the first 50 records ordered by id.
Repository:
#Repository
public interface MyRepository extends JpaRepository<MyEntity, String> {
Page<MyEntity> findAll(Pageable pageable);
}
Service:
#Service
public class MyDataService {
#Autowired
MyRepository myRepository;
private static final int LIMIT = 50;
public Optional<List<MyEntity>> getAllLimited() {
Page<MyEntity> page = myRepository.findAll(PageRequest.of(0, LIMIT, Sort.by(Sort.Order.asc("id"))));
return Optional.of(page.getContent());
}
}
Found the original idea here:
https://itqna.net/questions/16074/spring-data-jpa-does-not-recognize-sql-limit-command
(which will also link to another SO question btw)

#Query in JPA throwing null pointer exception

I am getting nullPointerException from my custom jpa method findShiftNameById() in my shift planner project(github link below). I have used #Query annotation inside ShiftDetailsRepo Interface to implement the method. Please check and let me know what could be causing this. I tried by removing nativeQuery parameter but that gives shift_details not mapped error.I tried by changing datatypes between int and long and also by changing sd.shiftName to sd.shift_name(as per the column name that is available in my actual database) but still same nullPointerException error is coming.
Github link-https://github.com/Anupam5713/shiftPlannerAPI
The method is being called in the ShiftPlanService Class inside the service package
Is there something wrong in my query?
#Query(value = "select sd.shiftName from shift_details sd where sd.shiftId=:shiftId",nativeQuery=true)
public String findShiftNameById(#Param("shiftId") int shiftId);
The following is the cause of NPE. The call to sdr is done before the dependencies are wired.
#Service
public class ShiftPlanService {
#Autowired
ShiftDetailsRepo sdr;
String S1 = sdr.findShiftNameById(1);
String S2 = sdr.findShiftNameById(2);
Move these initializations to a #PostConstruct method.
Example
#PostConstruct
public void initDetails() {
S1 = sdr.findShiftNameById(1);
S2 = sdr.findShiftNameById(2);
}

How to set values in ItemPreparedStatementSetter for one to many mapping

I am trying to use JdbcBatchItemWriter for a domain object RemittanceClaimVO . RemittanceClaimVO has a List of another domain object , ClaimVO .
public class RemittanceClaimVO {
private long remitId;
private List<ClaimVO> claims = new ArrayList<ClaimVO>();
//setter and getters
}
So for each remit id, there would be multiple claims and I wish to use single batch statement to insert all rows.
With plain jdbc, I used to write this object by putting values in batches like below ,
ist<ClaimVO> claims = remittanceClaimVO.getClaims();
if(claims != null && !claims.isEmpty()){
for(ClaimVO claim:claims){
int counter = 1 ;
stmt.setLong(counter++, remittanceClaimVO.getRemitId());
stmt.setLong(counter++, claim.getClaimId());
stmt.addBatch();
}
}
stmt.executeBatch();
I am not sure how to achieve same in Spring Batch by using ItemPreparedStatementSetter.
I have tried similar loop as above in setValues method but values not getting set.
#Override
public void setValues(RemittanceClaimVO remittanceClaimVO, PreparedStatement ps) throws SQLException {
List<ClaimVO> claims = remittanceClaimVO.getClaims();
for(ClaimVO claim:claims){
int counter = 1 ;
ps.setLong(counter++, remittanceClaimVO.getRemitId());
ps.setLong(counter++, claim.getClaimId());
}
}
This seems another related question.
Please suggest.

Custom Initialization Strategy for EF Code First that doesn't drop tables to add a column

The latest EF Code First NuGet package comes with a custom implementation of IDatabaseInitializer called DontDropDbJustCreateTablesIfModelChanged. As the name implies, when a model change is detected, it will not drop and recreate the whole database, just drop and recreate the tables.
Say I have this model class:
public class User
{
public string Username { get; set; }
// This property is new; the model has changed!
public string OpenID { get; set; }
}
How would one go about implementing an IDatabaseInitializer that doesn't drop any tables either. In this case, it would just add an OpenID column to the User table?
I think it is a matter of SQL. So for SQL Server you can write something like:
public class MyInitializer : IDatabaseInitializer<MyContext>
{
public void InitializeDatabase(MyContext context)
{
context.Database.SqlCommand(
#"
IF NOT EXISTS (SELECT 1 FROM sys.columns AS col
INNER JOIN sys.tables AS tab ON tab.object_Id = col.object_Id
WHERE tab.Name = 'User' AND col.Name = 'OpenId')
BEGIN
ALTER TABLE dbo.User ADD OpenId INT;
END");
}
}
But in the same way you can execute such script without adding it to your application which I think is much better approach.
With the current version of Code First, you cannot simply amend your schema and preserve any data that you might have in your tables. If maintaining data, such as reference data / lookup tables is important with this release you can create your own Initializer and override the Seed method to populate your tables
public class MyDbInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
protected override void Seed(MyContext context)
{
var countries = new List<Country>
{
new Country {Id=1, Name="United Kingdom"},
new Country{Id=2, Name="Ireland"}
};
countries.ForEach(c => context.Countries.Add(c));
}
}
And then use this in your Application_Start:
Database.SetInitializer<MyContext>(new MyDbInitializer());
I believe that this is being addressed currently by the EF Team, but wasn't ready for release at the time the Code First drop came out. You can see a preview here: Code First Migrations