MyBatis stored procedure call with optional parameters - mybatis

I wonder how could I describe in mybatis xml stored procedure call with optional parameters.
For example:
DAO
public List getMethod1(Object arg1) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("arg1", arg1);
return selectList("myBatisSelect", map);
}
public List getMethod1(Object arg1, Object arg2) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("arg1", arg1);
map.put("arg2", arg2);
return selectList("myBatisSelect", map);
}
XML
<select id="myBatisSelect"
parameterType="map"
resultType="MyResultObject">
CALL sql_stored_procedure
(
#arg1= #{arg1}
<!-- How to add optional parameter here??-->
)
</select>

You could use conditional logic to dynamically generate your sql.
i.e.
<select id="myBatisSelect"
parameterType="map"
resultType="MyResultObject">
CALL sql_stored_procedure
(
#arg1= #{arg1}
<if test="arg2 != null">
,#{arg2}
</if>
)
</select>
The MyBatis3 User Guide has more information.

Related

How to use #BeforeStep Job Parameters in JdbcCursorItemReader for named Query

I have the code like below
#Bean
public JdbcCursorItemReader<Map<String, Object>> itemReader() {
return new JdbcCursorItemReader<Map<String, Object>>() {
private JobParameters jobParameter;
String sql = "select EMPLOYEE_ID as empId, EMPLOYEE_NAME as empName EMPLOYEE_AGE as age from EMPLOYEE EMPLOYEE_DEPT =:empDept and EMPLOYEE_SAL > :empSal";
Map<String, Object> namedParameters = null;
#PostConstruct
public void initialize() throws Exception
{
setDataSource(dataSource);
setSql("select 1 from dual");
setRowMapper(new ColumnMapRowMapper());
}
#BeforeStep
public void retrieveExecutionContext(StepExecution stepExecution)
{
jobParameter = stepExecution.getJobParameters();
namedParameters = new HashMap<String, Object>() {
{
put("bstd", jobParameter.getString("empDept"));
put("bwtn", jobParameter.getString("empSal"));
}
};
jobParameter.getParameters().forEach((k, v) -> System.out.println("key =" + k + ", Value:" + v));
}
#Override
public void afterPropertiesSet() throws Exception {
setSql(NamedParameterUtils.substituteNamedParameters(sql, new MapSqlParameterSource(namedParameters)));
setPreparedStatementSetter(new ListPreparedStatementSetter(
Arrays.asList(NamedParameterUtils.buildValueArray(sql, namedParameters))));
setRowMapper(new ColumnMapRowMapper());
setDataSource(dataSource);
super.afterPropertiesSet();
}
};
}
Tried using calling afterPropertiesSet, but still seeing below exception
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter 'empDept': No value registered for key 'empDept'
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:361) ~[spring-jdbc-5.3.22.jar:5.3.22]
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:485) ~[spring-jdbc-5.3.22.jar:5.3.22]
Requirement is dynamic query, so don't have control of the Select query and the where conditions.
Thanks in advance,
You can use a SpEL expression to inject and use job parameters in your item reader bean definition as follows:
#Bean
#StepScope
public JdbcCursorItemReader<Map<String, Object>> itemReader(#Value("#{jobParameters['empDept']}") String empDept, #Value("#{jobParameters['empSal']}") String empSal) {
JdbcCursorItemReader<Map<String, Object>> itemReader = new JdbcCursorItemReader<>();
// use parameters 'empDept' and 'empSal' in your sql query as needed
return itemReader;
}
Note that the item reader should be step-scoped for that to work. For more details, please refer to the documentation: Late Binding of Job and Step Attributes.

Spring REST Endpoint Returning String instead of JSON

The following endpoint returns a username as a string.
How would I structure it to return a json object that contains a key with that string as its value (e.g., {"user":"joeuser"}?
#GetMapping(value = "/getUser", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> getUser() {
HttpHeaders responseHeaders = new HttpHeaders();
CustomUserAuthentication authentication = (CustomUserAuthentication) SecurityContextHolder.getContext().getAuthentication();
return ResponseEntity.ok().headers(responseHeaders).body(String.valueOf(authentication.getPrincipal()));
}
Using some Json library (like gson), build the Json object and return it in the body instead of the String. Make sure response content-type is application/json
You can also manually build the String that looks like Json but content to must be as above.
Spring can do what you want, but you need to return something that Spring needs to marshal into JSON. From my previous answer: https://stackoverflow.com/a/30563674/48229
#RequestMapping(value = "/json", method = RequestMethod.GET, produces = "application/json")
#ResponseBody
public Map<String, Object> bar() {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("test", "jsonRestExample");
return map;
}

How to return HashMap from JPA query?

I want to return a HashMap from JPA query like the below but I don't know how to fill the HashMap from this query. Actually I want to fill charts from HashMap in the frontend
public HashMap<String,String> getCount(Date start,Date end) {
HashMap<String, String> map=new HashMap<String, String>();
Query q =
em.createQuery(
"select count(i.uuid),i.username from Information i where i.entereddt between :start and :end group by i.username");
q.setParameter("start",new Timestamp(start.getTime()));
q.setParameter("end",new Timestamp(end.getTime()));
System.out.println(" query"+ q.getResultList().get(0).toString());
return map;
}
Any suggestions?
It appears that you were trying to execute a query which return types not mapped to any Java entities you have (or if they be present you never mentioned them). In this case, you want to use createNativeQuery(), which will return a List of type Object[].
Try using this version of the method:
public HashMap<String,String> getCount(Date start,Date end) {
HashMap<String, String> map=new HashMap<String, String>();
Query q = em.createNativeQuery(
"select count(i.uuid),i.username from Information i" +
"where i.entereddt between :start and :end group by i.username");
q.setParameter("start",new Timestamp(start.getTime()));
q.setParameter("end",new Timestamp(end.getTime()));
List<Object[]> list = query.getResultList();
for (Object[] result : list) {
map.put(result[0].toString(), result[1].toString());
}
return map;
}
Please refer, JPA 2.0 native query results as map
In your case in Postgres, it would be something like,
List<String> list = em.createNativeQuery("select cast(json_object_agg(count(i.uuid),i.username) as text) from schema.information i where i.entereddt between :start and :end group by i.username")
.setParameter("start",new Timestamp(start.getTime()))
.setParameter("end",new Timestamp(end.getTime()))
.getResultList();
//handle exception here, this is just sample
Map map = new ObjectMapper().readValue(list.get(0), Map.class);
Kindly note, I am just sharing my workaround with Postgres.
I know that it's an old question, but you can create an object to store info
public class UserCount {
private String username;
private Long count;
public UserCount(String user, Long count){
this.username = user;
this.count = count;
}
}
It's important to create the constructor and to pass the parameters in the correct way.
The JPQL became
select my.package.UserCount(i.username, count(i.uuid) ) from schema.information i where i.entereddt between :start and :end group by i.username
The query returns a List<UserCount> .

Converting query parameter from String to integer

I'm passing a HashMap
HashMap<String, String> paramsMap = new HashMap<String, String>();
paramsMap.put("jobXXX", "" + jobState.getCode());
paramsMap.put("rmi_auftrag_xxx", "rmi_auftrag_status");
to a MyBatis query:
<select id="loadRmiOrdersByTypeOrState" parameterType="map" resultMap="rmiJobMap">
<![CDATA[
SELECT rmi_auftrag_id as rmiJobId,
rmi_auftrag_typ as rmiJobType,
rmi_auftrag_status as rmiJobState,
rm_dokument_id as rmDocId
FROM lis_mgr.rmi_auftrag
WHERE #{rmi_auftrag_xxx} = #{jobXXX,javaType=String,jdbcType=NUMERIC}
]]>
</select>
The type of the jobXXX parameter is yet an integer. Is there a way to tell MyBatis to convert the integer-in-string-form into a "true" integer?
You can put your Integer as Integer into your HashMap and then run the query.
HashMap<String, Object> paramsMap = new HashMap<String, String>();
paramsMap.put("jobXXX", jobState.getCode());
paramsMap.put("rmi_auftrag_xxx", "rmi_auftrag_status");
method 1). user custom type handler
public class StringToIntTypeHandler extends BaseTypeHandler<String> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, Integer.valueOf(parameter));
}
//...
}
xml
#{jobXXX,typeHandler=xxx.StringToIntTypeHandler}
method 2). use ognl
<bind name="param" value="#java.lang.Integer#valueOf(jobXXX)" />
//...
where #{rmi_auftrag_xxx} = #{param}
or
${#java.lang.Integer#valueOf(jobXXX)}

ObjectDatasource passing parameters

I want to Insert data from textboxes using ObjectDatasource. The ObjectDataSource is bound to a gridview but displays certain computed columns only. The Textboxes are used to input all the basic inputs.
ObjectDatasource Delete & Select commands (Link buttons on gridview) are working. However I am having trouble with Insert command. I am not able to figure out how to pass the data from the textboxes as parameters to the ObjectDataSource Insert
EDIT: With the code below, a record is getting inserted. Parameters are getting passed. odssMain.Insert() gives the Error: "Object reference not set to an instance of an object".
EDIT: WHY AM I GETTING THIS ERROR?
Also the ObjectDataSource has been acting weird. After an error, I have to reconfigure the Insert Method again on the ODS Wizard as the method will be blank.
ASP.NET 3.5 & SQL 2008, VS 2008.
Here's my code:
<asp:ObjectDataSource ID="odsMain" runat="server"
SelectMethod="SelectMain" DeleteMethod="DeleteMain"
InsertMethod="InsertMain" UpdateMethod="UpdateMain"
OldValuesParameterFormatString="original_{0}" TypeName="MainDB" >
.......
<InsertParameters>
<asp:Parameter Name="Quantity" Type="Int32" />
</InsertParameters>
DAL FILE:
[DataObjectMethod(DataObjectMethodType.Insert)]
public static int InsertMain(int Quantity)/
{
SqlConnection con = new SqlConnection(GetConnectionString());
string strQuery = "INSERT INTO t_Main (Quantity) VALUES (#Quantity)";
SqlCommand cmd = new SqlCommand(strQuery, con);
cmd.Parameters.AddWithValue("#Quantity", Quantity);
con.Open();
int i = cmd.ExecuteNonQuery();
con.Close();
return i;
}
CODE BEHIND FILE:
protected void btnSaveAnalysis_Click(object sender, EventArgs e)
{
odsMain.InsertParameters.Clear();
//Store parameters with values to the collection
odsMain.InsertParameters.Add(new Parameter ("Quantity", TypeCode.Int32, iQuantity.ToString()));
//Diferent ways that I tried. Still not working
//odsMain.InsertParameters.Add("Quantity", iQuantity.ToString());
//odsMain.InsertParameters["Quantity"].DefaultValue = iQuantity.ToString();
odsMain.Insert();
}
you could try like this....
ObjectDataSource for InsertParameter looks like below one
<InsertParameters>
<asp:Parameter Name="FirstName" />
<asp:Parameter Name="MiddleName" />
<asp:Parameter Name="LastName" />
<asp:Parameter Name="Desgination" />
<asp:Parameter Name="Address" />
<asp:Parameter Name="City" />
<asp:Parameter Name="State" />
<asp:Parameter Name="Country" />
</InsertParameters>
I will also pass InsertMethod property of ObjectDataSource,which will have an InsertCustomer method.
InsertCustomer method looks like below one :-
public void InsertCustomer(string FirstName, string MiddleName,string LastName, string Desgination, string Address, string City, string State, string Country)
{
SqlConnection con = new SqlConnection(conStr);
SqlCommand cmd = new SqlCommand("InsertCustomer", con);
cmd.CommandType = CommandType.StoredProcedure;
//this check is necessary, when u don't pass any value as it will pass as [default] and will give error
if (string.IsNullOrEmpty(FirstName))
FirstName = string.Empty;
if (string.IsNullOrEmpty(LastName))
LastName = string.Empty;
if (string.IsNullOrEmpty(MiddleName))
MiddleName = string.Empty;
if (string.IsNullOrEmpty(Desgination))
Desgination = string.Empty;
if (string.IsNullOrEmpty(Address))
Address = string.Empty;
if (string.IsNullOrEmpty(City))
City = string.Empty;
if (string.IsNullOrEmpty(State))
State = string.Empty;
if (string.IsNullOrEmpty(Country))
Country = string.Empty;
cmd.Parameters.AddWithValue("#IV_FirstName", FirstName);
cmd.Parameters.AddWithValue("#IV_LastName", LastName);
cmd.Parameters.AddWithValue("#IV_MiddleName", MiddleName);
cmd.Parameters.AddWithValue("#IV_Desgination", Desgination);
cmd.Parameters.AddWithValue("#IV_Address", Address);
cmd.Parameters.AddWithValue("#IV_City", City);
cmd.Parameters.AddWithValue("#IV_State", State);
cmd.Parameters.AddWithValue("#IV_Country", Country);
using (con)
{
con.Open();
cmd.ExecuteNonQuery();
}
}
Button Save for inserting record.
//Insert record Save Button
protected void btnSave_Click(object sender, EventArgs e)
{
Customer.InsertParameters["FirstName"].DefaultValue = GetGridTextBoxValue("txtFirstName");
Customer.InsertParameters["MiddleName"].DefaultValue = GetGridTextBoxValue("txtMiddleName");
Customer.InsertParameters["LastName"].DefaultValue = GetGridTextBoxValue("txtLastName");
Customer.InsertParameters["Desgination"].DefaultValue= GetGridTextBoxValue("txtDesgination");
Customer.InsertParameters["Address"].DefaultValue = GetGridTextBoxValue("txtAddress");
Customer.InsertParameters["City"].DefaultValue = GetGridTextBoxValue("txtCity");
Customer.InsertParameters["State"].DefaultValue = GetGridTextBoxValue("txtState");
Customer.InsertParameters["Country"].DefaultValue = GetGridTextBoxValue("txtCountry");
Customer.Insert();
}
GetGridTextBoxValue function will get TextBox text value from footer row of respective column.
//Get TextBox value of GridView Footer Row
public string GetGridTextBoxValue(string txtID)
{
try
{
TextBox txt = (TextBox)gvCustomer.FooterRow.FindControl(txtID); // here you can place any text box value on your design page
return txt.Text;
}
catch (Exception ex)
{
return string.Empty;
throw ex;
}
}
and the results image is like this ...