can we optimize below code using generics - java

I have a Service class, which makes 2 different calls to another API which returns 2 different response objects. For Simplicity let that be shown as below
public class Service {
public AddResponseWrapper add(Request request) {
final AddResponseWrapper addResponseWrapper = new AddResponseWrapper();
try {
final AddResponse addResponse = addApi.add(request);
addResponseWrapper.setAddResponse(addResponse);
} catch (final Exception e) {
addResponseWrapper.setErrorDetails(convert(e));
}
return addResponseWrapper;
}
public DeleteResponseWrapper delete(Request request) {
final DeleteResponseWrapper deleteResponseWrapper = new DeleteResponseWrapper();
try {
final DeleteResponse deleteResponse = deleteApi.delete(request);
deleteResponseWrapper.setDeleteResponse(deleteResponse);
} catch (final Exception e) {
deleteResponseWrapper.setErrorDetails(convert(e));
}
return deleteResponseWrapper;
}
}
Similarly, I have these 2 below POJOs created
public class AddResponseWrapper {
private AddResponse addResponse;
private ErrorDetails errorDetails;
public AddResponse getAddResponse() {
return addResponse;
}
public void setAddResponse(final AddResponse addResponse) {
this.addResponse = addResponse;
}
public ErrorDetails getErrorDetails() {
return errorDetails;
}
public void setErrorDetails(final ErrorDetails errorDetails) {
this.errorDetails = errorDetails;
}
}
public class DeleteResponseWrapper {
private DeleteResponse deleteResponse;
private ErrorDetails errorDetails;
public DeleteResponse getDeleteResponse() {
return deleteResponse;
}
public void setDeleteResponse(final DeleteResponse deleteResponse) {
this.deleteResponse = deleteResponse;
}
public ErrorDetails getErrorDetails() {
return errorDetails;
}
public void setErrorDetails(final ErrorDetails errorDetails) {
this.errorDetails = errorDetails;
}
}
The AddResponseWrapper & DeleteResponseWrapper are classes that I have created, while the AddResponse and DeleteResponse classes are classes that I inherit from the API that I call.
Now if I make 2 more APi calls, for say Subtract & Multiply operations, I would have to create 2 more POJO classes SubtractResponseWrapper and MultiplyResponseWrapper.
I do not find this solution clean, what I would really like to have is a Generic class, that should be returned from each of these calls and I can avoid the use of multiple Pojo classes.

Try this - however without some sort of heirarchy in your response objects it's possibly going to be not much better than ResponseWrapper<Object>.
public class ResponseWrapper<T> {
private T response;
private ErrorDetails errorDetails;
public T getResponse() {
return response;
}
public void setResponse(final T response) {
this.response = response;
}
public ErrorDetails getErrorDetails() {
return errorDetails;
}
public void setErrorDetails(final ErrorDetails errorDetails) {
this.errorDetails = errorDetails;
}
}
Then your service becomes -
public class Service {
public ResponseWrapper<AddResponse> add(Request request) {
final ResponseWrapper<AddResponse> addResponseWrapper = new ResponseWrapper<AddResponse>();
try {
final AddResponse addResponse = addApi.add(request);
addResponseWrapper.setResponse(addResponse);
} catch (final Exception e) {
addResponseWrapper.setErrorDetails(convert(e));
}
return addResponseWrapper;
}
public ResponseWrapper<Delete> delete(Request request) {
final ResponseWrapper<Delete> deleteResponseWrapper = new ResponseWrapper<Delete>();
try {
final DeleteResponse deleteResponse = deleteApi.delete(request);
deleteResponseWrapper.setResponse(deleteResponse);
} catch (final Exception e) {
deleteResponseWrapper.setErrorDetails(convert(e));
}
return deleteResponseWrapper;
}
}
Without changing the API class you can't go much further on simplification.

Related

how can i specify the return type for asyncRabbitTemplate.convertSendAndReceiveAsType() at run time?

i've been struggeling with the following code. and am not sure how to deserialize it or even pass the correct type at run time.
the code is:
#Override
public <T, R> R sendAsync(T payload, String routingKey, String exchangeName) {
ListenableFuture<R> listenableFuture =
asyncRabbitTemplate.convertSendAndReceiveAsType(
exchangeName,
routingKey,
payload,
new ParameterizedTypeReference<>() {
}
);
try {
return listenableFuture.get();
} catch (InterruptedException | ExecutionException e) {
LOGGER.error(" [x] Cannot get response.", e);
return null;
}
}
let us say that am just calling the method like the following
SaveImageResponse response = backendClient.sendAsync( new SaveImageRequest(createQRRequest.getOwner(), qr), RabbitConstants.CREATE_QR_IMAGE_KEY, RabbitConstants.CDN_EXCHANGE);
while the pojo is the following:
public class SaveImageResponse {
private String id;
private String message;
public SaveImageResponse() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
return "SaveImageResponse{" +
"id='" + id + '\'' +
", message='" + message + '\'' +
'}';
}
}
the current code is throwing the following error:
Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class dev.yafatek.qr.api.responses.SaveImageResponse (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; dev.yafatek.qr.api.responses.SaveImageResponse is in unnamed module of loader 'app')
thanks in advance
SOLUTION:
so I ended up using the following:
#Override
public <T, R> R sendAsync(T payload, String routingKey, String exchangeName, Class<R> clazz) {
ListenableFuture<R> listenableFuture =
asyncRabbitTemplate.convertSendAndReceiveAsType(
exchangeName,
routingKey,
payload,
new ParameterizedTypeReference<>() {
}
);
try {
return objectMapper.convertValue(listenableFuture.get(), clazz);
} catch (InterruptedException | ExecutionException e) {
LOGGER.error(" [x] Cannot get response.", e);
return null;
}
}
by using the object mapper and pass the actual type when call the method using
Class<POJO> clazz
to use the above code :
WebsiteInfoResponse websiteInfoResponse = backendClient.sendAsync(new GetWebsiteInfoReq(createBusinessDetailsRequest.getWebsiteUrlId()), RabbitConstants.GET_WEBSITE_INFO_KEY, RabbitConstants.QR_EXCHANGE, WebsiteInfoResponse.class);
You can't.
The whole reason for ParameterizedTypeReference<Foo> is to tell the converter you want a Foo; this has to be resolved at compile time for the method; you can't call sendAsync() to receive different types.
Providing no generic type means it will convert to Object (usually a map).
Even new ParameterizedTypeReference<R>() { } won't work because R is not resolved at compile time for the generic type (of the sendAsync() method).
You have to do the conversion yourself.
#SpringBootApplication
public class So69299112Application {
public static void main(String[] args) {
SpringApplication.run(So69299112Application.class, args);
}
#Bean
MessageConverter converter() {
return new Jackson2JsonMessageConverter();
}
ObjectMapper mapper = new ObjectMapper();
#Bean
AsyncRabbitTemplate template(RabbitTemplate template) {
template.setMessageConverter(new SimpleMessageConverter());
return new AsyncRabbitTemplate(template);
}
#Bean
ApplicationRunner runner(Service service) {
return args -> {
byte[] response = service.sendAsync("bar", "foo", "");
Foo foo = this.mapper.readerFor(Foo.class).readValue(response);
System.out.println(foo);
};
}
#RabbitListener(queues = "foo")
public Foo listen(String in) {
return new Foo(in);
}
public static class Foo {
String foo;
public Foo() {
}
public Foo(String foo) {
this.foo = foo;
}
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
#Override
public String toString() {
return "Foo [foo=" + this.foo + "]";
}
}
}
#Component
class Service {
private static final Logger LOGGER = LoggerFactory.getLogger(Service.class);
AsyncRabbitTemplate asyncRabbitTemplate;
public Service(AsyncRabbitTemplate asyncRabbitTemplate) {
this.asyncRabbitTemplate = asyncRabbitTemplate;
}
public byte[] sendAsync(Object payload, String routingKey, String exchangeName) {
ListenableFuture<byte[]> listenableFuture = asyncRabbitTemplate.convertSendAndReceive(
exchangeName,
routingKey,
payload);
try {
return listenableFuture.get();
}
catch (InterruptedException | ExecutionException e) {
LOGGER.error(" [x] Cannot get response.", e);
return null;
}
}
}

How to store List<Object> in Room database? (I'm having trouble retrieving the list with DAO query)

I am storing Device objects in a Room database and I'm having a problem with retrieving one of the attributes (temp_values) that is a list of floats. I've followed other advice found on here that says you need a type converter so I've shown that here. When I try to compile I get this error:
"warning: The query returns some columns [temp_values] which are not
used by java.lang.Float. You can use #ColumnInfo annotation on the
fields to specify the mapping. You can suppress this warning by
annotating the method with
#SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by
the query: temp_values. Fields in java.lang.Float: ."
The issue is with the getTempValues query in the DAO, if I comment this out then everything compiles fine. I've included below the Device object, the TemperatureListConverter, and my DAO.
#Entity(tableName = "devices")
#TypeConverters(TemperatureListConverter.class)
public class Device implements Serializable {
#PrimaryKey
#NonNull
#ColumnInfo(name = "serial_num")
private String serialNum;
#ColumnInfo(name = "temp_values")
#TypeConverters(TemperatureListConverter.class)
private List<Float> tempValues;
public Device(String serialNum) {
this.serialNum = serialNum;
this.tempValues = new ArrayList<>();
}
public String getSerialNum() {
return serialNum;
}
public List<Float> getTempValues() {
return tempValues;
}
public void setTempValues(List<Float> tempValues) {
this.tempValues = tempValues;
}
}
public class TemperatureListConverter {
private static final Gson gson = new Gson();
#TypeConverter
public static List<Float> toTempList(String tempValuesString) {
if (tempValuesString == null) {
return Collections.emptyList();
}
Type type = new TypeToken<List<Float>>() {}.getType();
return gson.fromJson(tempValuesString, type);
}
#TypeConverter
public static String fromTempList(List<Float> tempValues) {
return gson.toJson(tempValues);
}
}
#Dao
#TypeConverters(TemperatureListConverter.class)
public interface DeviceDao {
#Query("SELECT * FROM devices")
List<Device> getAllDevices();
#Query("SELECT * FROM devices WHERE serial_num = :serialNum")
Device getDevice(String serialNum);
#Query("SELECT temp_values FROM devices WHERE serial_num = :serialNum")
List<Float> getTempValues(String serialNum);
#Query("UPDATE devices SET temp_values = :tempValues WHERE serial_num = :serialNum")
int setTempValues(String serialNum, List<Float> tempValues);
#Insert
void insert(Device... device);
#Query("DELETE FROM devices WHERE serial_num = :serialNum")
void deleteBySerial(String serialNum);
}
EDIT: I've added my database class here.
#Database(entities = {Device.class}, version = 37, exportSchema = false)
#TypeConverters(TemperatureListConverter.class)
public abstract class DeviceDatabase extends RoomDatabase {
private static final String DB_NAME = "devices_db";
private static DeviceDatabase deviceDb;
// simple singleton
public static DeviceDatabase getDeviceDb(Context context) {
if (deviceDb == null) {
deviceDb = Room.databaseBuilder(context, DeviceDatabase.class, DB_NAME)
.fallbackToDestructiveMigration()
.build();
}
return deviceDb;
}
public abstract DeviceDao getDeviceDao();
public void addDevice(final Device device) {
new Thread(new Runnable() {
#Override
public void run() {
try {
getDeviceDao().insert(device);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public void removeDevice(final String serialNum) {
new Thread(new Runnable() {
#Override
public void run() {
try {
getDeviceDao().deleteBySerial(serialNum);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public Device getDevice(final String serialNum) {
final Device[] finalDevice = new Device[1];
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
finalDevice[0] = getDeviceDao().getDevice(serialNum);
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
}
return finalDevice[0];
}
public List<Float> getTempValues(final String serialNum) {
final List<Float> finalTempValues = new ArrayList<>();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
finalTempValues.addAll(getDeviceDao().getTempValues(serialNum));
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
}
return finalTempValues;
}
public void setTempValues(final String serialNum, final List<Float>
tempValues) {
new Thread(new Runnable() {
#Override
public void run() {
try {
getDeviceDao().setTempValues(serialNum, tempValues);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
When Room processes a query that returns a collection type (List<Float> in this case), it tries to generate an implementation for a query that returns multiple rows of the table. That is the typical use, as in your query to get all devices:
#Query("SELECT * FROM devices")
List<Device> getAllDevices();
When a TypeConverter that returns a collection is used in a query intended to yield a single row, you need to give Room a hint of what you want. One way to do that is to wrap the collection value in a class:
public class ListWrapper {
#ColumnInfo(name = "temp_values")
List<Float> tempValues;
}
And change the query to return the wrapper class:
#Query("SELECT temp_values FROM devices WHERE serial_num = :serialNum LIMIT 1")
ListWrapper getTempValues(String serialNum);
I tried this with the code you posted. It eliminates the build error and appears to produce the desired implementation code.
It seems there is some issue while converting a generic type base. Try the following
#TypeConverter
public static List<Float> toTempList(String tempValuesString) {
if (tempValuesString == null) {
return Collections.emptyList();
}
Type type = new TypeToken<List<Float>>() {}.getType();
return gson.fromJson(tempValuesString, type);
}
#TypeConverter
public static String fromTempList(List<Float> tempValues) {
Type type = new TypeToken<List<Float>>() {}.getType();
return gson.toJson(tempValues, type);
}

Best way to use retrofit response in several activies

I have a function searchForTrips() which sends an API request and fetch some response in following way.
private void searchForTrips(){
int departurePortId = PORT_ID_LIST.get(departurePort);
int returnPortId = PORT_ID_LIST.get(returnPort);
int pax= Integer.parseInt(noOfPassengers);
String departureDatePARSED = DEPARTURE_DATE_VALUES.get(departureDate);
String returnDatePARSED = RETURN_DATE_VALUES.get(departureDate);
Call<TripSearchResponse> call = apiService.searchAvailableTrips(TripType,departurePortId,returnPortId,departureDatePARSED,returnDatePARSED,pax);
call.enqueue(new Callback<TripSearchResponse>() {
#Override
public void onResponse(Call<TripSearchResponse> call, Response<TripSearchResponse> response) {
int statusCode = response.code();
switch(statusCode){
case 200:
default:
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
break;
}
}
#Override
public void onFailure(Call<TripSearchResponse> call, Throwable t) {
Log.i(TAG, t.getMessage());
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
}
});
}
The purpose is to make this callback function reusable so I can call it from several activities and get requested data as I need. What is the best way to implement this?
try this way, its dynamic way and easy to use:
Create Retforit Interface:
public interface ApiEndpointInterface {
#Headers("Content-Type: application/json")
#POST(Constants.SERVICE_SEARCH_TRIP)
Call<JsonObject> searchForTrip(#Body TripRequest objTripRequest);
}
Create Retrofit Class:
public class AppEndPoint {
private static Retrofit objRetrofit;
public static ApiEndpointInterface getClient() {
if (objRetrofit == null){
objRetrofit = new Retrofit.Builder()
.baseUrl(Constants.SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return objRetrofit.create(ApiEndpointInterface.class);
}
}
Create this helper Classes/Interfaces to hold web service callback:
public enum ResponseState {
SUCCESS,
FAILURE,
NO_CONNECTION
}
public enum RequestType {
SEARCH_FOR_TRIP // add name for each web service
}
public class Response {
public ResponseState state;
public boolean hasError;
public RequestType requestType;
public JsonObject result;
}
public interface RestRequestInterface {
void Response(Response response);
Context getContext();
}
public class ResponseHolder { used to hold the Json response could be changed as your response
#SerializedName("is_successful")
#Expose
private boolean isSuccessful;
#SerializedName("error_message")
#Expose
private String errorMessage;
public boolean isSuccessful() {
return isSuccessful;
}
public void setSuccessful(boolean successful) {
isSuccessful = successful;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
public class AppClient {
private static ApiEndpointInterface objApiEndpointInterface;
private static Response objResponse;
private static Call<JsonObject> objCall;
// implement new method like below for each new web service
public static void searchForTrip(TripRequest objTripRequest, RestRequestInterface objRestRequestInterface) {
objResponse = new Response();
objResponse.state = ResponseState.FAILURE;
objResponse.hasError = true;
objResponse.requestType = RequestType.SEARCH_FOR_TRIP; // set type of the service from helper interface
objApiEndpointInterface = AppEndPoint.getClient();
objCall = objApiEndpointInterface.searchForTrip(objTripRequest);
handleCallBack(objRestRequestInterface);
}
private static void handleCallBack(final RestRequestInterface objRestRequestInterface) {
objCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
try {
ResponseHolder objResponseHolder = new Gson().fromJson(response.body(), ResponseHolder.class);
if (objResponseHolder.isSuccessful()) {
objResponse.state = ResponseState.SUCCESS;
objResponse.hasError = false;
objResponse.result = response.body();
} else {
objResponse.errorMessage = objResponseHolder.getErrorMessage();
}
objRestRequestInterface.Response(objResponse);
} catch (Exception objException) {
objResponse.errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
objRestRequestInterface.Response(objResponse);
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable objThrowable) {
String errorMessage = "";
if (objThrowable instanceof IOException) {
errorMessage = objRestRequestInterface.getContext().getString(R.string.no_connection_error);
} else {
errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
}
objResponse.errorMessage = errorMessage;
objRestRequestInterface.Response(objResponse);
}
});
}
}
then go to your activity of fragment and make the call like this:
public class MainActivity extends AppCompatActivity implements RestRequestInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize ids
// prepare to call web service
// 1.Initialize your object to be sent over web service
TripRequest objTripRequest = new TripRequest();
objTripRequest.id = 1;
// 2.Show loader
// 3.Make the call
AppClient.searchForTrip(objTripRequest, this);
}
#Override
public void Response(Response response) {
// hide loader
try {
if (response.state == ResponseState.SUCCESS && !response.hasError) {
// check the type of web service
if (response.requestType == RequestType.SEARCH_FOR_TRIP) {
// acces the return here from response.result
}
} else {
String errorMsg = response.hasError ? response.errorMessage : getString(R.string.no_connection_error);
// show the error to the user
}
} catch (Exception objException) {
// show the error to the user
}
}
#Override
public Context getContext() {
// do not forgit set the context here
// if fragment replace with getAcitvity();
return this;
}
}

Unit testing, custom Call class for retrofit2 request: Reponse has private access

When I create custom Call class I can't return Response, because Response class is final. Is there any workaround for this?
public class TestCall implements Call<PlacesResults> {
String fileType;
String getPlacesJson = "getplaces.json";
String getPlacesUpdatedJson = "getplaces_updated.json";
public TestCall(String fileType) {
this.fileType = fileType;
}
#Override
public Response execute() throws IOException {
String responseString;
InputStream is;
if (fileType.equals(getPlacesJson)) {
is = InstrumentationRegistry.getContext().getAssets().open(getPlacesJson);
} else {
is = InstrumentationRegistry.getContext().getAssets().open(getPlacesUpdatedJson);
}
PlacesResults placesResults= new Gson().fromJson(new InputStreamReader(is), PlacesResults.class);
//CAN"T DO IT
return new Response<PlacesResults>(null, placesResults, null);
}
#Override
public void enqueue(Callback callback) {
}
//default methods here
//....
}
In my unit test class I want to use it like this:
Mockito.when(mockApi.getNearbyPlaces(eq("testkey"), Matchers.anyString(), Matchers.anyInt())).thenReturn(new TestCall("getplaces.json"));
GetPlacesAction action = new GetPlacesAction(getContext().getContentResolver(), mockEventBus, mockApi, "testkey");
action.downloadPlaces();
My downloadPlaces() method look like:
public void downloadPlaces() {
Call<PlacesResults> call = api.getNearbyPlaces(webApiKey, LocationLocator.getInstance().getLastLocation(), 500);
PlacesResults jsonResponse = null;
try {
Response<PlacesResults> response = call.execute();
Timber.d("response " + response);
jsonResponse = response.body();
if (jsonResponse == null) {
throw new IllegalStateException("Response is null");
}
} catch (UnknownHostException e) {
events.sendError(EventBus.ERROR_NO_CONNECTION);
} catch (Exception e) {
events.sendError(EventBus.ERROR_NO_PLACES);
return;
}
//TODO: some database operations
}
After looking at retrofit2 Response class more thoroughly I've found out that there is a static method that do what I need. So, I simply changed this line:
return new Response<PlacesResults>(null, placesResults, null);
to:
return Response.success(placesResults);
Everything works now.

Spring AOP: java.util.HashMap cannot be cast to SomeClass

When I used spring aop(aspectJ proxy-target-class="true"), List can receive the parameters from json but it shows that "java.util.hashmap cannot be cast to MyEntity". And I am using SSH framework. The codes are shwon below:
package com.yoyo.aspect;
#Aspect
#Component
#Scope("prototype")
#ParentPackage("json-default")
#Results({#Result(name="result", type="json", params={"root","dataMap"})})
public class LoggerAspect extends SuperAction{
#Autowired
private LoggerService loggerService;
//signature
#Pointcut("execution(String com.yogo.action.admin..*.*(..))")
public void adminLogger(){}
#Pointcut("execution(String com.yogo.action.enterprise..*.*(..))")
public void enterpriseLogger(){}
#Pointcut("execution(String com.yogo.action.product..*.*(..))")
public void productLogger(){}
#Around(value="adminLogger()||enterpriseLogger()||productLogger()")
public Object loggerAround(ProceedingJoinPoint pjp) throws Throwable{
ServletRequestAttributes attr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
HttpSession session=attr.getRequest().getSession(true);
String className = pjp.getSignature().getDeclaringTypeName();
Method method = ((MethodSignature)pjp.getSignature()).getMethod();
String methodName = method.getName();
Logger log = new Logger();
log.setClassName(className);
log.setMethodName(methodName);
try {
log.setResult("success");
return pjp.proceed();
} catch (Throwable throwable) {
log.setResult("failure");
return RESULT;
} finally {
if(session!=null&&session.getAttribute("AID")!=null){
log.setAid((int)session.getAttribute("AID"));
log.setDatetime(new Date());
loggerService.save(log);
}
}
}
}
package com.yoyo.action.product.property;
#Controller
#Scope("prototype")
#ParentPackage("json-default")
#Namespace("/product")
#Results({#Result(name="result", type="json", params={"root","dataMap"})})
#InterceptorRefs({#InterceptorRef(value="json"), #InterceptorRef(value="defaultStack")})
public class AddOrUpdateProductPropertyAction extends SuperAction /*implements ModelDriven<ProductProperty>*/{
#Autowired
private ProductPropertyService productPropertyService;
private ProductProperty productProperty = new ProductProperty();
private List<ProductProperty> list;
private Product product;
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public List<ProductProperty> getList() {
return list;
}
public void setList(List<ProductProperty> list) {
this.list = list;
}
/*#Override
public ProductProperty getModel() {
return productProperty;
}*/
#Action(value="AddOrUpdateProductProperty")
public String addOrUpdateProductProperty() throws Throwable{
int pid = product.getPid();
try {
if(pid!=0&&!"".equals(pid)){
productProperty.setPid(pid);
for(int i=0; i<list.size(); i++){
productProperty.setPpid(list.get(i).getPpid());
productProperty.setPkey(list.get(i).getPkey());
productProperty.setPvalue(list.get(i).getPvalue());
productProperty.setStatus(list.get(i).getStatus());
productPropertyService.saveOrUpdate(productProperty);
}
}else{
setErrorResult(2, "No Product ID");
}
} catch (Exception e){
setErrorResult(1, e.getMessage());
throw new Exception(e);
}
return RESULT;
}
}
And I post the json in the right format to this AddOrUpdateProductProperty(url):
{
"product":{
"pid":"1"
},
"list":[
{
"pkey": "",
"pvalue":"45%",
"status":1
}]
}
the response saying(exception.getMessage):
{"msg":"java.util.HashMap cannot be cast to com.yogo.entity.ProductProperty","flag":1}
If I comment the AOP execution, everything runs well and all the data in "list" can be well received and written in mysql successfully.
//#Around(value="adminLogger()||enterpriseLogger()||productLogger()")
I also checked receiving the data in "list" using println. I found that it can receive it but cannot cast to MyEntity "ProductProperty":
System.out.println(list.get(i));
//this works
System.out.println(list.get(i).getPpid());
//this got error as above
I am not quite familiar with spring aop mechanism, anyone could help? Thank you so much!

Categories