java Spring JDBCTemplate - where clause - spring

In my JDBC training, I have a question on the use of the where clause.
Suppose that i have a table in my db that i want manage with a spring application using a jdbc template, let's assume "Logbase", with this column: host, user, clientip. Suppose now that i want allow query db based on a single column for all column, that is:
Select * from Logbase where host = x
and
Select * from Logbase where user = y
and
Select * from Logbase where clientip = z
I suppose I must write a separated java method for every of this query, something like this:
public Logbase getLogbaseFromHost(String id)
{
String SQL = "select * from Logbase where host = ?";
Logbase logbase = (Logbase) jdbcTemplate.queryForObject(SQL, new Object[]{id},
(rs, rowNum) -> new Logbase(rs.getString("host"), rs.getString("user"),
rs.getInt("clientip")));
return logbase;
}
public Logbase getLogbaseFromUser(String id)
{
String SQL = "select * from Logbase where user = ?";
Logbase logbase = (Logbase) jdbcTemplate.queryForObject(SQL, new Object[]{id},
(rs, rowNum) -> new Logbase(rs.getString("host"), rs.getString("user"),
rs.getInt("clientip")));
return logbase;
}
public Logbase getLogbaseFromClientIP(String id)
{
String SQL = "select * from Logbase where clientip = ?";
Logbase logbase = (Logbase) jdbcTemplate.queryForObject(SQL, new Object[]{id},
(rs, rowNum) -> new Logbase(rs.getString("host"), rs.getString("user"),
rs.getInt("clientip")));
return logbase;
}
Now, if i want allow query db based on 2 parameters, i suppose i must write a method for the 3 possible pair of parameters (one for clientip-user, another for clientip-host and the last for user-host).
Finally, if i want allow query db selecting all the parameters, i must write another method with the where clause in the query that ask for all variables.
If I did not say heresies and everything is correct, i have 7 method. But, i the number of parameters and combinations grow, this may be a problem. There is a way to get around it?
Note: for work reasons, i cant use Hibernate or other ORM framework. I MUST use jdbc.
Tnx to all for patience and response.

The solution could be based on SQL
Select *
from Logbase
where
(? is null or host = ?)
AND (? is null or user = ?)
AND (? is null or clientip = ?)
jdbcTemplate.queryForObject(SQL, new Object[]{host, host, user, user, clienttip, clienttip}
So e.g. if user is not specified (user is null - true) all the records are included

So, you also can use org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
** Select *
from Logbase where
(:host is null or host = :host)
AND (:user is null or user = :user)
AND (:clientip is null or clientip = :clientip)**
And java code:
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("host", host);
params.addValue("user", user);
params.addValue("clientip", clientip);
namedParameterJdbcTemplate.queryForObject(sqlQuer, params);

Related

Spring batch dynamic IN Query

The following ItemReader get a list of thousands accounts that need to be retrieved from MD database.
In this approach I am limited to the number of accounts that I can use:
#StepScope
#Bean
public ItemReader<OmsDto> itemReader(#Value("#{stepExecutionContext[accOms]}") List<String> notLoadedFiles) {
StringBuffer buffer = new StringBuffer();
notLoadedFiles.forEach(accountNumber -> buffer.append("'"+accountNumber+"',"));
buffer.replace(buffer.length()- 1, buffer.length(), "");
DriverManagerDataSource mdDataSource = new DriverManagerDataSource();
mdDataSource.setDriverClassName("prestosql");
mdDataSource.setUrl("jdbc:presto:....");
mdDataSource.setUsername(".....");
mdDataSource.setPassword("....");
String sql ="SELECT DISTINCT "
.....
.....
+ "FROM MD.ONLINE WHERE acct IN ";
JdbcCursorItemReader<OmsDto> reader = new JdbcCursorItemReader<OmsDto>();
reader.setVerifyCursorPosition(false);
reader.setDataSource(mdDataSource);
reader.setSql(sql);
reader.open(new ExecutionContext());
BeanPropertyRowMapper<OmsDto> rowMapper = new BeanPropertyRowMapper<>(OmsDto.class);
rowMapper.setPrimitivesDefaultedForNullValue(true);
reader.setRowMapper(rowMapper);
return reader;
}
What is the correct way to create dynamic IN Query (WHERE A IN (…, .., …)) ?
Thank you
Here is an example to generate IN query dynamically,
Example Query: SELECT * FROM USER WHERE ID IN (?,?,?,?,?)
List ids = List.of(1,2,3,4,5);
String inParams = String.join(",", ids.stream().map(id -> "?").collect(Collectors.toList()));
String query = String.format("SELECT * FROM USER WHERE ID IN (%s)", inParams);
Note that, if your query IN clause parameters limit more than 1000, it's better to use TEMP tables. Here some examples on github

Doing subquery with list of Ids using Dapper 1.50 and Oracle 18

All,
I am sending a list of Ids( More than the max of 1000 allowed by Oracle) in the sql where clause using Dapper 1.50 and Oracle 18. Since there is a limit on the number of items in the in clause, I decided to do a sub query as below but I can't get this to working. Can someone shed some light on this. I will always be sending more than 1000 items as Ids. The second sql statement is not working( It says invalid sql).
public static List<Notes> GetNotes()
{
List<Notes> notes = new List<Notes>();
try
{
using (var connection = OracleConnectionString)
{
connection.Open();
string idCommand = #"select * from (select
id
from
note_text
order by
1
desc)where
rownum <=2000";
var notesList = connection.Query<Notes>(idCommand);
var noteIds1 = notesList .Select(i => i.id).ToList();
string command = #"select
tnt.id as id,
tnt.NOTE_TXT
from
note_text tnt
(
select
note_id
from
dual
where
note_id in :noteIds1
)x where x.note_id = tnt.id";
var info = connection.Query<Notes>(command, new
{
noteIds1
});
notes = info.ToList();
}
}
catch (Exception ex)
{
}
return notes;
}
Where does this list of id's originate? I'd get them into a file that can be accessed as an external table. Then, instead of
select ...
from ...
where id in (<listthatstoolong)
you would
select ...
from ...
where id in (select id from id_external_table)
Then you are not limited to a size of a string of elements in your IN list.

Spring JDBC : Inconsistent results when performing Order By

Any help would be greatly appreciated. I am working on a project using Spring JDBC for data access and am performing a simple query with an order by column expression, I am currently getting inconsistent results meaning the order by doesn't seem to be working. I have tried more than one database still no avail.
String sql = "select * from account where upper(name) like upper(:query) order by name asc";
MapSqlParameterSource params = new MapSqlParameterSource().addValue("query", "%" + query + "%");
List<Account> accountsSearched = namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper<Account>(Account.class));
Any ideas what could be the issue?
So the problem is not within your SQL code, but problem exist in search method implementation
Existing Code
public List<Account> search(String uncleanedQuery, int offset) {
String query = uncleanedQuery.replaceAll("([-+.^:,])","");
String sql = "select * from account where upper(name) like upper(:query) order by name asc";
MapSqlParameterSource params = new MapSqlParameterSource().addValue("query", "%" + query + "%");
List<Account> accountsSearched = namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper<Account>(Account.class));
Set<Account> accountSearchSet = new HashSet<Account>(accountsSearched);
List<Account> accounts = new ArrayList<Account>(accountSearchSet);
return accounts;
}
In the above code, we are fetching data correctly but assigning it to HashSet. HashSet does not respect ordering by name and generates random order for Account, due to which you are getting random order every time.
Solution 1:
There is no reason, you actually need Set. Using set just making your program slow. If you want to get DISTINCT data then modify SQL query.
public List<Account> search(String uncleanedQuery, int offset) {
String query = uncleanedQuery.replaceAll("([-+.^:,])","");
String sql = "select * from account where upper(name) like upper(:query) order by name asc";
MapSqlParameterSource params = new MapSqlParameterSource().addValue("query", "%" + query + "%");
List<Account> accountsSearched = namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper<Account>(Account.class));
return accountsSearched;
}
Solution 2:
Still, you want to go with your approach then change code to use TreeSet and order based on the name
public List<Account> search(String uncleanedQuery, int offset) {
String query = uncleanedQuery.replaceAll("([-+.^:,])", "");
System.out.println("Search Query Called");
String sql = "select * from account where upper(name) like upper(:query) order by name";
MapSqlParameterSource params = new MapSqlParameterSource().addValue("query", "%" + query + "%");
List<Account> accountsSearched = namedParameterJdbcTemplate.query(sql, params,
new BeanPropertyRowMapper<Account>(Account.class));
Comparator<Account> comp = new Comparator<Account>() {
#Override
public int compare(Account a1, Account a2) {
return a1.getName().compareTo(a2.getName());
}
};
SortedSet<Account> accountSearchSet = new TreeSet<Account>(comp);
accountSearchSet.addAll(accountsSearched);
List<Account> accounts = new ArrayList<Account>(accountSearchSet);
return accounts;
}

Query to check if the record exists in Spring Jdbc Template

I am fairly new to spring ,I am looking to check if a certain email id exists in database or not , using Spring Jdbc Template ,I looked here but could'nt find the proper answer .I am looking something like ,SELECT count(*) from table where email=?
Any help will be appreciated.
You can do something as below if you are using jdbctemplate and new version of spring
private boolean isEmailIdExists(String email) {
String sql = "SELECT count(*) FROM table WHERE email = ?";
int count = jdbcTemplate.queryForObject(sql, new Object[] { email }, Integer.class);
return count > 0;
}
queryForObject method of jdbcTemplate accepts the sql query as the first parameter, second argument is an array of objects for the sql query place holders and the third argument is the expected return value from the sql query.
In this case we only have one place holder and hence I gave the second argument as new Object[] { email } and the result we are expecting is a count which is a Integer and hence I gave it as Integer.class
I kind of got this answer from https://www.mkyong.com/spring/jdbctemplate-queryforint-is-deprecated/
You can go through it if you are interested.
private boolean isEmailIdExists(String email) {
return jdbcTemplate.queryForObject("SELECT EXISTS(SELECT FROM table WHERE email = ?)", Boolean.class, email);
}
http://www.postgresqltutorial.com/postgresql-exists/

Unable to get table alias working with spring jdbcTemplate

I am trying select statement on a table with alias name. When I retrieve the resultset the alias doesn't seem to work with it.item_id. It works with item_id. Any idea where I am going wrong?
getJdbcTemplate().query((connection) -> {
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM some_item_table AS it WHERE it.item_id = ?");
preparedStatement.setString(1, 123);
return preparedStatement;
}, (rs, i) -> product()
.setId(rs.getInt("it.item_id"))// NOT WORKING
//.setId(rs.getInt("item_id")) THIS WORKS!
...
);
In the result set the aliases are not available so it does not work.
You could change your SQL query like SELECT it.item_id AS some_item_id * FROM some_item_table AS it then you can do rs.getInt("some_item_id").
In your query you do not really need an alias as you have only one table.

Resources