I have looked at a number of StackOverflow questions on this issue but can't find one that makes any sense. This one comes closest but doesn't show how to get the return value from the function.
Here's my mapper call:
public Long callMyFunction(#Param("recordId") Long recordId, #Param("otherId") Long otherId, #Param("date") Date date, #Param("comments") String comments);
Here's the mapper XML:
<select id="callMyFunction" parameterType="map" statementType="CALLABLE" resultType="java.lang.Long">
{ #{resultId,javaType=java.lang.Long,jdbcType=NUMERIC,mode=OUT} = call MYSCHEMA.MYPACKAGE.my_function(
#{recordId,jdbcType=NUMERIC,mode=IN},
#{otherId,jdbcType=NUMERIC,mode=IN},
#{date,jdbcType=DATE,mode=IN},
#{comments,jdbcType=VARCHAR,mode=IN})}
</select>
The call works, but the return value (resultId) is always null.
Can anybody spot the problem?
If you want to return directly the result, then the SQL call must be:
SELECT MYSCHEMA.MYPACKAGE.my_function(...) FROM DUAL
If you want to keep with calling the function in the procedure call style, that means the result is an OUT parameter (you env declared it OUT).
The minimum change would consist in adding a parameter to the mapper method signature:
public Long callMyFunction(#Param("recordId") Long recordId, #Param("otherId") Long otherId, #Param("date") Date date, #Param("comments") String comments, #Param("resultIdContainer") Map<String, Long> resultIdContainer);
In the XML: forget the resultType, this is for selects. And the call:
{ #{resultIdContainer.resultId, javaType=java.lang.Long,jdbcType=NUMERIC,mode=OUT} = call ...
Not that I use here a map to contain the resutlId: an indirection is required: the function will write the parameter 'result' value somewhere you can read later (after your mapper call), you can also use a class with a resultId property.
Related
I would like to know if there is a way, without writing the explicit query itself, to have my method query and filter using only one parameter instead of two.
Example, I have this method and both parameters in my method are the same values
public interface FooRepository extends JpaRepository<Foo, Long> {
Optional<Foo> findByFromRangeLessThanEqualAndToRangeGreaterThanEqual(int same, int value);
}
The above method works just fine, but how can I write the method name so that I only need to have one parameter/input, instead of two, since the parameters will always be the same values?
In this case I am basically trying to see if a value is between (inclusive between) one value.
So if my fromRange is 1 and my toRange is 5, then if the input is 5, is should return the result.
I've defined a class for CRUD operations on comments. The read method is overloaded.
class Comment{
// method 1: returns all the comments by a user
findAll(long userId, long subjectId, String param);
// method 2: returns all the comments of all the users
findAll(long subjectId, String param)
}
The point cut expression I've tried is
#Around("execution(* com.package..*Controller.findAll(..)) && args(userId,subjectId,..)")
public Object validateFindAll(final ProceedingJoinPoint proceedingJoinPoint, final long userId, final long subjectId) {
// validate userId, if available
// validate subjectId
}
Problem: As the data types for userId and subjectId are same, the point expression when applied to method 2 shifts the param values by 1 place. This means, the expression does not understand that the first parameter userId isn't passed. Instead, userId gets 'subjectId' as value and the subjectId gets the adjacent parameter 'param' as its value.
Note
I am trying to avoid writing another method like findUserComments().
I want to maintain consistency across the application. There are other classes with similar patterns of CRUD operations.
Question: Is it possible to define an expression applicable to both the methods with the first parameter userId being optional ?
EDIT - Solution
While I was playing around with different approaches as suggested in the solutions below, I've finally removed method 2. I handle that case in method 1.
You cannot explicitly bind an AspectJ parameter and then expect it to match an incompatible signature. Thus, your pointcut will only match findAll(long, long, ..), i.e. "method 1" in your example. You can specify optional arguments with .., but then you cannot bind them to named parameters.
For example, it is possible to match both methods and bind long subjectId and String param via args(.., subjectId, param) because both parameters are predictably right-aligned at the end of the signature. If you want any optional (and thus unbound) parameter, you need to use thisJoinPoint.getArgs():
#Around("execution(* com.package..*Controller.findAll(..)) && args(.., subjectId, param)")
public Object validateFindAll(
final ProceedingJoinPoint thisJoinPoint,
final long subjectId,
final String param
) {
if (thisJoinPoint.getArgs().length == 3)
System.out.println(thisJoinPoint + " -> " + thisJoinPoint.getArgs()[0] + ", " + subjectId + ", " + param);
else
System.out.println(thisJoinPoint + " -> " + subjectId + ", " + param);
// (...)
}
But while getArgs() is dynamic, it probably is slower than parameter binding because it uses reflection. Maybe having two pointcuts is not so bad after all. If your advice method does complicated things before/after proceed(), you can still factor those things out into helper methods and call them from both advice.
Problem is related to method averloading actually. Since, you are passing long userId and long subjectId AOP will always try to match those arguments. Solutions could be
1) Create another pointcut for other argument i.e. 1 for long,long and other for long, String
2) Use variable argument signature in the begining such as
#Around("execution(* com.org..findAll(..)) && args(..,subjectId,param)")
public Object validateFindAll(final ProceedingJoinPoint joinPoint, final long userId, final long subjectId) {
}
instead of using variable argument in the begining. Then you can use getArgs() method to figure out arguments.
This is simple solution but may slowdown your processing.
3) Though as a design issue, I would suggest to encapsulate all your parameters in one single object and pass it. Instead of passing multiple parameters. It will help you in future as well.
Now I want to calculate the average concentration of pm2.5 these days in each city through stored procedure .The first input parameter of the stored procedure is cityname string like Beijing,NewYork,and the other input parameters are beginTime and endTime.And the output class is called AvgPM25.
public class AvgPM25{
public String CityName{get;set;}
public decimal AvgValue{get;set;}
}
I pass parameters like Beijing,NewYork,20140801,20140802,it calculate and output the result.Now my problem is sometimes I try to change the parameters like search time,and the input parameters are like Beijing,NewYork,20130801,20140802,but it still output the result which is the previous search result.Is this problem due to the cache or concurrentcy?I try to set identity of 'CityName' of the AvgPM25 class property to be True,and set the cache policy to be NoCache,and set the concurrency mode to be Changed but still have this problem?How to fix it?
Although you change the search time,but the identity is not changed.Change the output class below may help you.Or you can try to set identity of the AvgPM25 class property to be True?
public class AvgPM25{
//identity:true,cache:default
public String CityName{get;set;}
public decimal AvgValue{get;set;}
//identity:true,cache:default,conbined with the two strings,like '2013080120140802'
public string SearchTIme{get;set;}
}
I've been using JoSQL for quite a few months now and today I came across a problem I am not sure how to solve. I probably could solve it by binding variables/placeholders, but I'd like to include the fields in the query.
SELECT * FROM ...MyObject WHERE getType != com.mypackage.myclass.TYPE_A
This is the query that I have. TYPE_A is a public static final int attribute in "myclass" class. Accessing methods (such as getType) is easy, because getType is expected to be a method from MyObject - just that I do not write round brackets after it (this is how JoSQL works as far as I know).
Does anyone happen to have an idea how to access a public static final field?
JoSQL uses gentlyweb-utils; it seems to be some sort of Accessor/Getter/Setter framework. I'd love to access that attribute without having to bind variables, but I haven't been able to do so.
Thanks for your help in advance! I really appreciate it.
I think I have figured something out. First: it seems not possible to access the static variables for whatever reason. I've used the following approach to solve my issue:
create a method, which picks up a given JoSQL-statement
mark the constants, which you want to replace, by say "{?FULL_PACKAGE_AND$CONSTANT}"
use reflections to determine the column as well as the column (and value) from the field
iteratively replace the statement until no "{?"-values are available
Example:
JoSQL-statement looks like this:
(isWeapon = TRUE AND getItem.getType2 = {?com.l2jserver.gameserver.model.items.L2Item$TYPE2_WEAPON})
Method using the query-object:
final Query query = DataLayer.createJoSqlQuery(joSql);
Method (pre)processing the JoSQL-statement:
final Query query = new Query();
int variableColumn = 0;
while (joSql.indexOf("{?") > -1) {
variableColumn++;
final int startIndex = joSql.indexOf("{?");
final int endIndex = joSql.indexOf("}", startIndex);
final String value = joSql.substring(startIndex + 2, endIndex);
try {
final Object variableValue = Class.forName(value.split("\\$")[0]).getField(value.split("\\$")[1]).get(null);
query.setVariable(variableColumn, variableValue);
joSql = joSql.replace("{?" + value + "}", "?");
}
catch (...) {
e.printStackTrace();
}
}
query.parse(joSql);
return query;
The JoSQL-statement preprocessing method bascially iterates through a given JoSQL-statement and sees whether it contains the string "{?". If it does, it does some copy and paste (note the dollar-symbol right in front of the constant name).
Finally it creates the objects and sets them using something similar to prepared statements "setObject"-method. In the end it just replaces the values within the JoSQL-statement with question marks ("?") and sets a corresponding object in the newly created Query-object, which is later used to retrieve information.
I have written a function that returns a User Defined Type.
How can i return a empty UDT in case of any error from the function?
I tried setting function to 'Nothing',but it is throwing 'Object Required' error.
Thanks in advance.
If at all possible, use a Class/Object instead. Even doing something as simple as turning this type:
Public Type EmpRecord
FName As String
LName As String
HiredDate As Date
End Type
into a class can be done by adding a Class to your project called EmpRecord and including just this in it:
Public FName As String
Public LName As String
Public HiredDate As Date
Then you can return Nothing from a function that gets an error while retrieving the record.
In VB6, a user-defined type is a "value type", while a class is a "reference type". Value types are typically stored on the stack (unless they're a member of a class). Reference types are stored as a pointer on the stack pointing to a place in the heap where the actual instance data is stored.
That means a reference to a class can be Nothing (the pointer is zero), whereas a value type can't.
There are multiple ways to solve your problem:
Add a Boolean member to your user-defined type to indicate success or failure.
Create another "wrapper" UDT like (see below) and return it from your function.
Change your UDT into a class, which can be Nothing (as tcarvin said).
Write the function to return a Boolean and take a ByRef parameter. The results are written to the parameter passed in if the function result is True. (A lot of people don't like this, but it's a common solution you should be aware of.)
Wrapper:
Public Type Wrapper
Success As Boolean
Inner As YourOriginalUDT
End Type
Function with ByRef:
Function Foo(ByRef Result As YourOriginalUDT) As Boolean
...
If Success Then
Foo = True
Result.A = A
Result.B = B
Result.C = C
... etc. ...
End If
End Function
A UDT can't be empty.
You can either use a "dummy" unintialised UDT, or just set all it's members back to the default values.