I am connecting to a mysql db from standalone java application. My application uses several filters that determine, which data will be selected from db.
In some cases, I would like to construct the select command in a way, that its "WHERE" parameter is ignored and selects all values from db instead.
This is my example:
String query = "SELECT * from messages WHERE type='" + type + "' ORDER BY id DESC";
The variable type can contain some specific type that matches the Varchar of the items in my db. However, a user can set the type to "all values" (or something like that, I hope this is clear enough), in which case, the query would select ALL values from db (it will ignore the where parameter).
I know I could do this by simply putting some if statements in my code and call a different select command in every branch, but this would be highly ineffective in case that several specifications (attributes inside WHERE parameter) are used.
For example:
String query = "SELECT * from messages WHERE type='" + type + "' AND time='" + time + "' ORDER BY id DESC";
I am not sure whether this is even possible to do. If not, sorry about dumm question... Thanks in advance!
I think you will have to do it through code, nothing in SQL to do what you want to do. Typically, people use ORM like Hibernate and construct the query in more secure way (to avoid SQL injection) instead of using String concatenation.
This is how it is done in Hibernate: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html#querycriteria-narrowing
You could create a Type class and a Time class and so on. This classes would contain a function called getSQL() which returns the type or in case of all types "".
The WHERE clause let you filter the dataset according to several predicates. Not specifying it means no filtering, hence all possible values for any predicates you would add.
If you consider the WHERE clause as a predicate, and predicates as either sets of predicates or atomic predicates, you can easily produce your clause by walking through the predicate nest and generate the needed string. If the string comes back empty, just elide the WHERE clause altogether.
interface Predicate {
public String toString();
}
// further derive this class for specific predicates
class AtomPredicate implements Predicate {
public AtomPredicate(){}
public toString() {}
}
// basic predicate set
class SetPredicate implements Predicate {
public SetPredicate(String connector){this.connector = connector;}
private ArrayList<Predicate> set;
private String connector;
public toString(){
String res, tmp;
int i;
if (set.size() == 0) return "";
while (res.size() == 0 && i < set.size()) {
res = set[i++].toString();
}
for (; i < set.size(); ++i) {
tmp = set[i].toString();
if (tmp.size() > 0)
res += connector + " " + tmp;
}
if (res.size() > 0)
return "(" + res + ")";
}
class WhereClause {
public WhereClause() {}
private Predicate predicate;
public toString(){
String res = predicate.toString();
if (res.size() > 0) return "WHERE " + res;
return "";
}
}
You can start from that basic outline, and expand as needed. You should however try to look for an existing solution first, like the jboss library linked in another answer, to avoid reinventing the wheel.
Related
I am trying to check whether the room name in that building exists or not.If exists, then don't save else save the data into the table.So I have written this query.
#Query(value = "select exists(select name,building from meetingroom " +
"where building=?1 and name=?2)",nativeQuery = true)
Boolean existsByRoom(String building,String name);
Now it is showing BigInteger cannot be cast to class java.lang.Boolean
Is there any way to solve this without effecting or changing the query.
Simplest solution is here is to use JPA derived query.
Assuming your entity is MeetingRoom
Boolean existsMeetingRoomByBuildingAndName(String building, String name);
One solution is to use java.util.Optional like below. And you need to use BigInteger instead of Boolean because query will return BigInteger value when record exist.
#Query(value = "select exists(select name,building from meetingroom " +
"where building=?1 and name=?2)",nativeQuery = true)
Optional<BigInteger> existsByRoom(String building,String name);
And in service layer, you can check if the value is present.
Optional<BigInteger> result =
meetingRoomRepository.existsByRoom(meetingRoomRequest.getBuilding(),meetingRoomRequest.getName());
if(result.isPresent()){
return true;
}
return false;
This is my Model class
//Model
public class CustomerData {
private String locomotive_id;
private String customer_name;
private String road_number;
private String locomotive_type_code;
private String in_service_date;
private String part_number;
private String emission_tier_type;
private String airbrake_type_code;
private String lms_fleet;
private String aar_road;
private String locomotive_status_code;
// Getters and Setters
Here is my RowMapper implementation
//RowMapper
public class CustomerDataResponseMapper implements RowMapper {
#Override
public Object mapRow(ResultSet rs, int count) throws SQLException {
CustomerData customerData = new CustomerData();
customerData.setLocomotive_id(rs.getString("locomotive_id"));
customerData.setCustomer_name(rs.getString("customer_name"));
customerData.setRoad_number(rs.getString("road_number"));
customerData.setLocomotive_type_code(rs.getString("locomotive_type_code"));
customerData.setIn_service_date(rs.getString("in_service_date"));
customerData.setPart_number(rs.getString("part_number"));
customerData.setEmission_tier_type(rs.getString("emission_tier_type"));
customerData.setAirbrake_type_code(rs.getString("airbrake_type_code"));
customerData.setLms_fleet(rs.getString("lms_fleet"));
customerData.setAar_road(rs.getString("aar_road"));
customerData.setLocomotive_status_code(rs.getString("locomotive_status_code"));
return customerData;
}
}
And finally, I got my DaoImpl class here
//DaoImpl
public String getCustomersData(String locoId, String custName, String roadNumber) {
CustomerData resultSet = null;
String str = "";
if (locoId != null && locoId.length() > 0 && !(locoId.equals("0"))) {
str = "select locomotive_id,customer_name,road_number,model_type as locomotive_type_code,to_char(in_service_date,'yyyy-mm-dd') as in_service_date,loco_part_number as part_number, emission_tier_type as emission_tier_type, "
+ "air_brake_type as airbrake_type_code,lms_fleet,aar_road,locomotive_status_code from get_rdf_explorer.get_rdf_locomotive_detail where locomotive_id = ?";
resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(), locoId);
} else if ((custName != null && custName.length() > 0)
&& (roadNumber != null && roadNumber.length() > 0 && roadNumber != "0")) {
str = "select locomotive_id,customer_name,road_number,model_type as locomotive_type_code,to_char(in_service_date,'yyyy-mm-dd') as in_service_date,loco_part_number as part_number, emission_tier_type as emission_tier_type, "
+ "air_brake_type as airbrake_type_code,lms_fleet,aar_road,locomotive_status_code from get_rdf_explorer.get_rdf_locomotive_detail where customer_name = ? and road_number= ?";
resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(), custName, roadNumber);
} else {
str = "select distinct customer_name from get_rdf_explorer.get_rdf_locomotive_detail order by customer_name asc";
resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper());
}
return resultSet.toString();
}
How can I conditionally get the values from the resultSet based on whether a particular column is present in the resultSet or not. As I am not getting all the columns all the time through my queries.
I am getting SQL bad grammar exception when specific column is not present in resultSet. For example when the third query to get distinct customer names get executed, in the resultSet only customerName would be there, but not the other columns.
It would be really a great help. Thanks a lot in advance.
Since you already have 3 separate queries why not have 3 separate RowMappers, one for each query. Your queries "know" what columns they return, so you can easily create those classes for RowMapper.
If you really want High-Fidelity solution you could create abstract base RowMapper for common parts and 3 subclasses for parts specifig to the query.
You can use a generic method which investigates the ResultSet's columns
#SuppressWarnings("unchecked")
public <T> T getColIfPresent(ResultSet rs, String columnName) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
if (columnName.equals(metaData.getColumnName(i))) {
return (T) rs.getObject(columnName);
}
}
return null;// not present
}
Then, in row mapper.
customerData.setLocomotive_id(getColIfPresent(rs, "locomotive_id"));
...
This has O(n*m) complexity where n - the number of columns checked, m - the number of columns returned in ResultSet.
If you choose to ignore the SQLException, at least log it in DEBUG or TRACE level in case a different subtype of SQLException occurs, so that it's not lost.
Rather than conditionnaly getting columns, you could modify your SQL to match your mapper, like setting other field to empty string or null (I don't remmember if getString() crashes on null or something).
For example your third query would look like:
select distinct customer_name, null as "locomotive_id",'' as "road_number", null as model_type, [etc.] from get_rdf_explorer.get_rdf_locomotive_detail order by customer_name asc
So each query would have the same columns and you don't have to adapt. This is the solution if you d'ont really want/can't change the rowMapper (or want to have only one for this object).
But honestly I would go with ikketu's solution. You should make a separate mapper for the thrid query (plus, it wouldn't be complicated). Not goign with an ORM is a choice but you'll have redundancy problem anyway. I would even add that you should separate some of the logic in your code, this methods seems to be doing different thing (business logic depending on input, and database access) it's not very clear (after the third if, create a method like "getdistinctName()" or something).
Santosh, a quick workaround could be passing a flag to your rowmapper while supplying it to jdbcTemplate. I've done so many times to avoid multiple rowmapper.
resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(1), custName, roadNumber);
For the above changes, you need to overload constructor with the default one. Then you need to use your flag i.e. instance variable in mapRow() method to handle each situation separately.
You can use BeanPropertyRowMapper which will directly map field names of target class. Here is the javadoc.
The names are matched either directly or by transforming a name separating the parts with underscores to the same name using "camel" case. So, you can use it any other classes whenever you want to map directly to a class. Just have to make sure selected fields are remain in target class. And a default or no-arg constructor.
Following example to get CustomerData using BeanPropertyRowMapper
RowMapper<CustomerData> mapper = new BeanPropertyRowMapper<>(CustomerData.class);
List<CustomerData> result = jdbc.query("your query string...", mapper, query_args...);
So, then you can return first object or whatsoever.
My advice is to split your getCustomersData into three different methods. If you definitely want to ignore this advice, the quick and dirty solution is to protect the rs.getString(...) calls inside your rowMapper. Something like this:
try {
customerData.setLocomotive_id(rs.getString("locomotive_id"));
} catch (SQLException e) {
// ignore this exception. DO NOT TRY THIS AT HOME!
}
If numbers of columns are not fix then you should go with ColumnMapRowMapper based on its implementation even you do not require to create separate concrete class of RowMapper (i.e. CustomerDataResponseMapper ) you just need to pass instance of ColumnMapRowMapper in query as given below:
ColumnMapRowMapper rowMapper = new ColumnMapRowMapper();
List<Map<String, Object>> customerDataList = jdbcTemplate.query(sql,rowMapper, args);
Now you should create one method to manipulate this map like
private CustomerData fillCustomerDataFromMap(List<Map<String, Object>> customerDataList){
CustomerData customerData = new CustomerData();
for(Map<String, Object> map: customerDataList ){
customerData.setColumn(map.get("columnName"));
customerData.setColumn(map.get("columnName"));
customerData.setColumn(map.get("columnName"));
customerData.setColumn(map.get("columnName"));
.........
.........
.........
}
return customerData;
}
This is more readable and remove the boilerplate codes and not throw any exception if column name is not present in map (it will simply returns null if column name is not present in map)
Reference of ColumnMapRowMapper :
https://github.com/spring-projects/spring-framework/blob/master/spring-jdbc/src/main/java/org/springframework/jdbc/core/ColumnMapRowMapper.java
Based on the logic of your queries i see that before executing sql query in get_rdf_explorer.get_rdf_locomotive_detail there are some records and 3 options of getting of necessary (unique) record are possible:
by locomotive_id
by customer_name and road_number
ANY record (all records must have same customer_name, else SQL distinct without any conditions return more than 1 row)
So, in the 3rd option you can get any 1 record with all attributes equal to NULL and NOT NULL customer_name value:
str = "select null as locomotive_id, customer_name, null as road_number,
<other attributes> from get_rdf_explorer.get_rdf_locomotive_detail where
rownum = 1";`
I need to create a JOOQ SELECT query dynamically based on the set of parameters. I dont know how to append it dynamically.
Please help
Thanks in advance.
jOOQ has two types of APIs to construct queries.
The DSL API that allows for creating inline SQL statements in your Java code, e.g.
create.select(T.A, T.B).from(T).where(T.X.eq(3).and(T.Y.eq(5)));
The "model" API that allows for incremental SQL building. At any time, you can access the "model" API through the getQuery() method on a DSL query object
An example of what you want to do is given in the manual here:
https://www.jooq.org/doc/latest/manual/sql-building/sql-statements/dsl-and-non-dsl/
For instance, optionally adding a join:
DSLContext create = DSL.using(configuration);
SelectQuery query = create.selectQuery();
query.addFrom(AUTHOR);
// Join books only under certain circumstances
if (join)
query.addJoin(BOOK, BOOK.AUTHOR_ID.equal(AUTHOR.ID));
Result<?> result = query.fetch();
Or, optinally adding conditions / predicates:
query.addConditions(BOOK.TITLE.like("%Java%"));
query.addConditions(BOOK.LANGUAGE_CD.eq("en"));
UPDATE: Given your comments, that's what you're looking for:
// Retrieve search strings from your user input (just an example)
String titleSearchString = userInput.get("TITLE");
String languageSearchString = userInput.get("LANGUAGE");
boolean lookingForTitles = titleSearchString != null;
boolean lookingForLanguages = languageSearchString != null;
// Add only those conditions that the user actually provided:
if (lookingForTitles)
query.addConditions(BOOK.TITLE.like("%" + titleSearchString + "%"));
else if (lookingForLanguages)
query.addConditions(BOOK.LANGUAGE_CD.eq(languageSearchString));
Note, you can also use the Field.compare(Comparator, Object) methods:
// Initialise your dynamic arguments
Field<String> field = BOOK.TITLE;
Comparator comparator = Comparator.LIKE;
String value = "%" + titleSearchString + "%";
// Pass them to the field.compare() method
query.addConditions(field.compare(comparator, value));
For more info, consider the org.jooq.SelectQuery Javadoc
If I have a method to create SQL queries, as below :
public List selectTuple() {
boolean status = true;
String query = "SELECT ";
query += getFields() == null ? " * " : " " + getFields() + " ";
query += " FROM " + tables;
if ( getSearchClause() != null ) {
query += " " + getSearchClause();
}
query += ";";
Debug("SQL...........caleed selectTuple method, query is : "+ query);
setQuery(query);
if ( getPrepared() ) {//If this is a Prepared query,
status = setPreparedStatement();
} else {
status = setNonPreparedStatement();
}
if ( ! status ) {
Log("[CRITICAL] (.........)..........");
}
status = doExecuteQuery();
if ( ! status ) {
Log("[CRITICAL] (.........)..........");
}
return( getResults() );
}//method selectTuple
However, since this will be used for different tables, the fields will be of different data types (int, string, date etc). So how can I iterate through such a ResultSet ?
Also, how can I create such an Insert query ?
Thanks.
Yes, I think it could be done... You can use getMetaData() in the ResultSet to get the number and type of columns and iterate through the ResultSet consequently.
getMetaData():
ResultSetMetaData class
However, I don't know how to code such a generic insert query...
You will need to use getObject and pass a map of JDBC to Java object mappings for your own types if any.
So, if your table has column i numeric and column s varchar the next code
ResultSet rs = st.executeQuery("select i, s from test");
rs.next();
System.out.println(rs.getObject(1).getClass());
System.out.println(rs.getObject(2).getClass());
will result in
class java.lang.Integer
class java.lang.String
the only thing for you to do is to check the returned Object for its class, using instanceof, to do the actual casting.
You can refer to this article for more details.
For the insert part you could use setObject method and rely on JDBC conversion, which is probably not a very good idea but should work.
As #jahroy commented, avoid using JDBC for this type of generic things. I also just had the same problem and I came up with an easy and elegant way to do it with JPA
Here's a generic method I've created to handle any SELECT query, you can get the idea how JPA works. In this case I just wanted to make sure there were only SELECT queries but you can make it more generic, to accept INSERT, DELETE, UPDATE...
public List executeSelectQuery(String selectQuery, Class entityClass, String classPersistentUnit)
{
selectQuery = selectQuery.toUpperCase();
if(!selectQuery.startsWith("SELECT"))
{
throw new IllegalArgumentException("This method only accepts SELECT queries. You tried: " + selectQuery);
}
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(classPersistentUnit);
EntityManager entityManager = entityManagerFactory.createEntityManager();
Query query = entityManager.createNativeQuery(selectQuery, entityClass);
return query.getResultList();
}
The good thing is that Netbeans and Eclipse (and probably more IDEs too, but I just use these two) come up with an "auto-create" class Entity, on the fly! You don't need to code anything at all on these entity classes. An entity class, briefly, represents a table in your database. Therefore each table in your database will be represented by an entity class.
You can find an easy and small tutorial here
We'd like to use only annotations with MyBatis; we're really trying to avoid xml. We're trying to use an "IN" clause:
#Select("SELECT * FROM blog WHERE id IN (#{ids})")
List<Blog> selectBlogs(int[] ids);
MyBatis doesn't seem able to pick out the array of ints and put those into the resulting query. It seems to "fail softly" and we get no results back.
It looks like we could accomplish this using XML mappings, but we'd really like to avoid that. Is there a correct annotation syntax for this?
I believe the answer is the same as is given in this question. You can use myBatis Dynamic SQL in your annotations by doing the following:
#Select({"<script>",
"SELECT *",
"FROM blog",
"WHERE id IN",
"<foreach item='item' index='index' collection='list'",
"open='(' separator=',' close=')'>",
"#{item}",
"</foreach>",
"</script>"})
List<Blog> selectBlogs(#Param("list") int[] ids);
The <script> element enables dynamic SQL parsing and execution for the annotation. It must be very first content of the query string. Nothing must be in front of it, not even white space.
Note that the variables that you can use in the various XML script tags follow the same naming conventions as regular queries, so if you want to refer to your method arguments using names other than "param1", "param2", etc... you need to prefix each argument with an #Param annotation.
I believe this is a nuance of jdbc's prepared statements and not MyBatis. There is a link here that explains this problem and offers various solutions. Unfortunately, none of these solutions are viable for your application, however, its still a good read to understand the limitations of prepared statements with regards to an "IN" clause. A solution (maybe suboptimal) can be found on the DB-specific side of things. For example, in postgresql, one could use:
"SELECT * FROM blog WHERE id=ANY(#{blogIds}::int[])"
"ANY" is the same as "IN" and "::int[]" is type casting the argument into an array of ints. The argument that is fed into the statement should look something like:
"{1,2,3,4}"
Had some research on this topic.
one of official solution from mybatis is to put your dynamic sql in #Select("<script>...</script>"). However, writing xml in java annotation is quite ungraceful. think about this #Select("<script>select name from sometable where id in <foreach collection=\"items\" item=\"item\" seperator=\",\" open=\"(\" close=\")\">${item}</script>")
#SelectProvider works fine. But it's a little complicated to read.
PreparedStatement not allow you set list of integer. pstm.setString(index, "1,2,3,4") will let your SQL like this select name from sometable where id in ('1,2,3,4'). Mysql will convert chars '1,2,3,4' to number 1.
FIND_IN_SET don't works with mysql index.
Look in to mybatis dynamic sql mechanism, it has been implemented by SqlNode.apply(DynamicContext). However, #Select without <script></script> annotation will not pass parameter via DynamicContext
see also
org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
org.apache.ibatis.scripting.xmltags.DynamicSqlSource
org.apache.ibatis.scripting.xmltags.RawSqlSource
So,
Solution 1: Use #SelectProvider
Solution 2: Extend LanguageDriver which will always compile sql to DynamicSqlSource. However, you still have to write \" everywhere.
Solution 3: Extend LanguageDriver which can convert your own grammar to mybatis one.
Solution 4: Write your own LanguageDriver which compile SQL with some template renderer, just like mybatis-velocity project does. In this way, you can even integrate groovy.
My project take solution 3 and here's the code:
public class MybatisExtendedLanguageDriver extends XMLLanguageDriver
implements LanguageDriver {
private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
script = matcher.replaceAll("(<foreach collection=\"$1\" item=\"__item\" separator=\",\" >#{__item}</foreach>)");
}
script = "<script>" + script + "</script>";
return super.createSqlSource(configuration, script, parameterType);
}
}
And the usage:
#Lang(MybatisExtendedLanguageDriver.class)
#Select("SELECT " + COLUMNS + " FROM sometable where id IN (#{ids})")
List<SomeItem> loadByIds(#Param("ids") List<Integer> ids);
I've made a small trick in my code.
public class MyHandler implements TypeHandler {
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
Integer[] arrParam = (Integer[]) parameter;
String inString = "";
for(Integer element : arrParam){
inString = "," + element;
}
inString = inString.substring(1);
ps.setString(i,inString);
}
And I used this MyHandler in SqlMapper :
#Select("select id from tmo where id_parent in (#{ids, typeHandler=ru.transsys.test.MyHandler})")
public List<Double> getSubObjects(#Param("ids") Integer[] ids) throws SQLException;
It works now :)
I hope this will help someone.
Evgeny
Other option can be
public class Test
{
#SuppressWarnings("unchecked")
public static String getTestQuery(Map<String, Object> params)
{
List<String> idList = (List<String>) params.get("idList");
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM blog WHERE id in (");
for (String id : idList)
{
if (idList.indexOf(id) > 0)
sql.append(",");
sql.append("'").append(id).append("'");
}
sql.append(")");
return sql.toString();
}
public interface TestMapper
{
#SelectProvider(type = Test.class, method = "getTestQuery")
List<Blog> selectBlogs(#Param("idList") int[] ids);
}
}
In my project, we are already using Google Guava, so a quick shortcut is.
public class ListTypeHandler implements TypeHandler {
#Override
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, Joiner.on(",").join((Collection) parameter));
}
}
In Oracle, I use a variant of Tom Kyte's tokenizer to handle unknown list sizes (given Oracle's 1k limit on an IN clause and the aggravation of doing multiple INs to get around it). This is for varchar2, but it can be tailored for numbers (or you could just rely on Oracle knowing that '1' = 1 /shudder).
Assuming you pass or perform myBatis incantations to get ids as a String, to use it:
select #Select("SELECT * FROM blog WHERE id IN (select * from table(string_tokenizer(#{ids}))")
The code:
create or replace function string_tokenizer(p_string in varchar2, p_separator in varchar2 := ',') return sys.dbms_debug_vc2coll is
return_value SYS.DBMS_DEBUG_VC2COLL;
pattern varchar2(250);
begin
pattern := '[^(''' || p_separator || ''')]+' ;
select
trim(regexp_substr(p_string, pattern, 1, level)) token
bulk collect into
return_value
from
dual
where
regexp_substr(p_string, pattern, 1, level) is not null
connect by
regexp_instr(p_string, pattern, 1, level) > 0;
return return_value;
end string_tokenizer;
You could use a custom type handler to do this. For example:
public class InClauseParams extends ArrayList<String> {
//...
// marker class for easier type handling, and avoid potential conflict with other list handlers
}
Register the following type handler in your MyBatis config (or specify in your annotation):
public class InClauseTypeHandler extends BaseTypeHandler<InClauseParams> {
#Override
public void setNonNullParameter(final PreparedStatement ps, final int i, final InClauseParams parameter, final JdbcType jdbcType) throws SQLException {
// MySQL driver does not support this :/
Array array = ps.getConnection().createArrayOf( "VARCHAR", parameter.toArray() );
ps.setArray( i, array );
}
// other required methods omitted for brevity, just add a NOOP implementation
}
You can then use them like this
#Select("SELECT * FROM foo WHERE id IN (#{list})"
List<Bar> select(#Param("list") InClauseParams params)
However, this will not work for MySQL, because the MySQL connector does not support setArray() for prepared statements.
A possible workaround for MySQL is to use FIND_IN_SET instead of IN:
#Select("SELECT * FROM foo WHERE FIND_IN_SET(id, #{list}) > 0")
List<Bar> select(#Param("list") InClauseParams params)
And your type handler becomes:
#Override
public void setNonNullParameter(final PreparedStatement ps, final int i, final InClauseParams parameter, final JdbcType jdbcType) throws SQLException {
// note: using Guava Joiner!
ps.setString( i, Joiner.on( ',' ).join( parameter ) );
}
Note: I don't know the performance of FIND_IN_SET, so test this if it is important
I had done this with postgresql.
#Update('''
UPDATE sample_table
SET start = null, finish = null
WHERE id=ANY(#{id});
''')
int resetData(#Param("id") String[] id)
ANY works like the IN.
Code above is using groovy but can be converted into java by replacing the single quotes into double.