Exchange RestTemplate giving 400 Bad Request - resttemplate

I am using exchange for GET method in RestTemplate. But while passing the requestEntity in the exchange method i am getting 400-Bad Request.
Below is the code I am using.
Producing an URL:
#GetMapping("/getValues")
public ResponseEntity<ValueResponse> getValueCall(#RequestBody AccountDTO accountDTO) {
Map<Integer, ValueEntity> crmMap = new HashMap<Integer, ValueEntity>();
int accountNumber = accountDTO.getAccountNumber();
valueMap.put(1, new ValueEntity("A", "B", "Tier 1", "DU", "Indian"));
valueMap.put(2, new CRMEntity("C", "D", "Tier 2", "DU", "American"));
valueMap.put(3, new CRMEntity("E", "F", "Tier 3", "DU", "English"));
valueMap.put(4, new CRMEntity("G", "H", "Tier 1", "DU", "Australian"));
ValueResponse valueResponse = new ValueResponse();
for (Map.Entry<Integer, ValueEntity> entry : valueMap.entrySet()) {
if (entry.getKey().equals(accountNumber)) {
valueResponse.setAccountNumber(entry.getKey());
valueResponse.setFirstName(entry.getValue().getFirstName());
valueResponse.setLastName(entry.getValue().getLastName());
valueResponse.setCustomerTier(entry.getValue().getCustomerTier());
valueResponse.setCustomerType(entry.getValue().getCustomerType());
valueResponse.setNationality(entry.getValue().getNationality());
return new ResponseEntity<ValueResponse>(valueResponse, HttpStatus.OK);
}
}
return null;
}
Consuming the URL:
public EnrollResponse enrollingUser(ValueDTO valueDTO) {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String,
String>();
headers.add("Content-Type", "application/json");
HttpEntity<ValueDTO> requestEntity = new HttpEntity<ValueDTO>(valueDTO,
headers);
ResponseEntity<ValueResponse> enrollResponseEntity =
restTemplate.exchange("http://localhost:8080/getValues",
HttpMethod.GET, requestEntity, ValueResponse.class);
return enrollResponseEntity.getBody();
}
Entity and DTO:
public class ValueDTO {
private String firstName;
private String lastName;
private String dateOfBirth;
private String gender;
private String nationality;
private String email;
private String channelId;
private int accountNumber;
private String languageKnown;
//constructor
//getters and setters
}
public class ValueResponse {
private long accountNumber;
private String firstName;
private String lastName;
private String customerTier;
private String customerType;
private String nationality;
//constructor
//getters and setters
}
public class ValueEntity {
private String firstName;
private String lastName;
private String customerTier;
private String customerType;
private String nationality;
//constructor
//getters and setters
}
I tried accessing the producer URL from postman with Header as Content-Type:application/json and Body:
{
"firstName":"A",
"lastName" : "B",
"dateOfBirth" : "12/12/12",
"gender" : "Female",
"nationality" : "Indian",
"email" : "abc#gmail.com",
"channelId" :"Values App",
"accountNumber" : 1,
"languageKnown" : "English"
}
as the valueDto object.
Console:
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 null
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:79)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:122)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:579)
at com.smilesmember.domain.AccountDomain.enrollingUser(AccountDomain.java:184)
at com.smilesmember.controller.SmilesMemberController.enrollWS(SmilesMemberController.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)

try the below code if it accepts ValueDTO as you mentioned
ResponseEntity<ValueResponse> enrollResponseEntity =
restTemplate.exchange("http://localhost:8080/getValues",
HttpMethod.GET, requestEntity, new ParameterizedTypeReference<ValueResponse>() {});

Related

Can we set the disable conversion value from config in #Converter

#Convert(converter = MsisdnEncryptor.class,disableConversion=true)
I have used this converter in my spring boot entity class.
package com.example.demo.entity;
This is the entity class:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name="employee")
public class Employee {
#Id
#Column(name = "emp_id")
#SequenceGenerator(
name = "employee_sequence",
sequenceName = "employee_sequence",
allocationSize = 1
)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "employee_sequence")
private Long EmpId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "mobile_number")
**#Convert(converter = MsisdnEncryptor.class,disableConversion=true)**
**here I want to put values from application.properties in disableConversion**
private String mobileNumber;
#Column(name= "date_time")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MM-yyyy HH:mm:ss", timezone="GMT+5:30")
private Timestamp dateTime;
}
This is the Encryptor class:
#Component
public class MsisdnEncryptor implements AttributeConverter<String, String> {
private static final Logger logger = LoggerFactory.getLogger(MsisdnEncryptor.class);
#Value("${is.enabled:true}")
private boolean isEnabled;
private static final String AES = "AES";
private static final String SECRET = "secret-key-12345";
private final Key key;
private final Cipher cipher;
public MsisdnEncryptor() throws Exception {
key = new SecretKeySpec(SECRET.getBytes(), AES);
cipher = Cipher.getInstance(AES);
}
#Override
public String convertToDatabaseColumn(String attribute) {
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.getEncoder().encodeToString(cipher.doFinal(attribute.getBytes()));
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
throw new IllegalStateException(e);
}
}
#Override
public String convertToEntityAttribute(String dbData) {
try {
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64.getDecoder().decode(dbData)));
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
throw new IllegalStateException(e);
}
}
}
I know we can use #Value for taking the values , but it doesn't work here as it says attribute value must be constant.
Or if there is any other way of doing the encryption and enabling/disabling it from config , I would be more than happy to go through it.

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.healthinsurance.entity.MdcnData[] out of VALUE_STRING token

I am trying to consume online API when i got this exception
this sample of JSON
> "[{\"mdcnNo\":\"2\",\"grpName\":\"VARIOUS\",\"gnrcName\":\"Acetylcysteine
> 200 mg/ mL in 10? mL Inj. NMSF Cilol®\",\"trdName\":\"Cilol 200mg/ml
> Inj.\",\"apprv\":\"لا يحتاج
> موافقة\",\"gndr\":\"كلاهما\",\"unit\":\"Amp.\",\"ATCcode\":\"V03AB23\",\"ADMR\":\"\",\"dfnDayDos\":\"\",\"inInsurance\":\"1\",\"bnfCopayment\":\"25%\",\"corpCopayment\":\"75%\",\"sts\":\"1\"}]"
Model Class
#JsonIgnoreProperties(ignoreUnknown = true)
public class MdcnData {
private String mdcnNo;
private String grpName;
private String gnrcName;
private String trdName;
private String apprv;
private String gndr;
private String unit;
private String ATCcode;
private String ADMR;
private String dfnDayDos;
private String inInsurance;
private String bnfCopayment;
private String corpCopayment;
private String sts;
//setter getter toString
service class
public List<MdcnData> getAllMdcnData() {
return Arrays.stream (restTemplate.getForObject(endPoint,MdcnData[].class))
.collect(Collectors.toList());
}
controller class
#GetMapping(value="/get", produces = MediaType.APPLICATION_JSON_VALUE)
public void run() {
List<MdcnData> allMdcnData = consumerSrivce.getAllMdcnData();
for (MdcnData mdcnData : allMdcnData) {
System.out.println(allMdcnData);
}

Eclipse RCP, bind a set to a map of properties

I have the following model:
public class HoverDescriptor {
private String id;
private String label;
private boolean enabled;
private String description;
private int stateMask;
private String modifierString;
public HoverDescriptor(String id, String label, boolean isEnabled, String description,
...
}
public class HoverViewModel {
Set<HoverDescriptor> mHovers;
private HoverViewModel() {
mHovers = new HashSet<HoverDescriptor>();
mHovers.add(new HoverDescriptor("Mock1", "Label1", true, "Desc1", SWT.DEFAULT, ""));
mHovers.add(new HoverDescriptor("Mock2", "Label2", false, "Desc2",SWT.DEFAULT, ""));
mHovers.add(new HoverDescriptor("Mock3", "Label3", false, "Desc3", SWT.DEFAULT, ""));
}
public static HoverViewModel newInstance() {
return new HoverViewModel();
}
public Set<HoverDescriptor> getHovers() {
return mHovers;
}
I would like to bind:
final IObservableSet checkedElements = ViewersObservables.observeCheckedElements(viewer, HoverDescriptor.class);
to the following ObservableMap:
IBeanSetProperty descriptors = PojoProperties.set("hovers", HoverDescriptor.class);
IObservableMap observeMap = PojoObservables.observeMap(descriptors.observe(fViewModel), HoverDescriptor.class, "enabled");
But, except from the deprecated MappedSet and DataBindingContext.bindSet(?) I couldn't find any idiomatic way.
How can I effectively do it?

Spring-Security DAOAuthenticationProvider asks for ID for authentication

I am using Spring-Security on top of a Spring-MVC application. I have my own implementation of UserDAO, userDetailsService. I am trying to authenticate by checking from database(ofcourse). I reach till the login page, everything seems to be working fine, but when I login, I get an error :
ERROR:
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: Provided id of the wrong type for class com.WirTauschen.model.User. Expected: class java.lang.Integer, got class java.lang.String
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:110)
I do not know where am I retrieving or working with user's id. I am posting the code below.
LoginService :
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService{
#Autowired private UserDao userDao;
#Autowired private Assembler assembler;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails = null;
User user = userDao.findByName(username);
if(user == null) { throw new UsernameNotFoundException("Wrong username or password");} //Never specify which one was it exactly
return assembler.buildUserFromUserEntity(user);
}
}
Assembler
#Service("assembler")
public class Assembler {
#Transactional(readOnly = true)
User buildUserFromUserEntity(com.WirTauschen.model.User userEntity){
String username = userEntity.getUsername();
String password = userEntity.getPassword();
// int id = userEntity.getId();
boolean enabled = userEntity.isActive();
boolean accountNonExpired = userEntity.isAccountNonExpired();
boolean credentialsNonExpired = userEntity.isCredentialsNonExpired();
boolean accountNonLocked = userEntity.isAccountNonLocked();
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
User user1 = new User(username,password,enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,authorities);
return user1;
}
}
User :
#Entity
#Table(name="registration")
public class User implements UserDetails{
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");
#Id
#Column(name="id")
private String id=UUID.randomUUID().toString();
// #GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "my_entity_seq_gen")
//#SequenceGenerator(name ="my_entity_seq_gen", sequenceName = "MY_ENTITY_SEQ")
#OneToMany
private Set<ProductBasic> productBasic;
#Column(name = "email")
private String email;
#Column(name = "username")
private String Username;
#Column(name = "displayname")
private String DisplayName;
#Column(name = "password")
private String password;
#Column(name = "companyname")
private String CompanyName;
#Column(name = "firstname")
private String FirstName;
#Column(name = "middlename")
private String MiddleName;
private String role="ROLE_USER";
#Transient
private final String PERMISSION_PREFIX = "ROLE_USER";
#Transient
private List<GrantedAuthority> authorities;
public User() {
this.authorities = new ArrayList<GrantedAuthority>();
authorities.add(USER_AUTH);
}
public User(String Username, String password, String Role){
this.Username = Username;
this.password = password;
this.role = Role;
if((role == null) || role.isEmpty()){ role = "ROLE_USER";}
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
Update sequence code
#Id
#Column(name = "sortcanvasid")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sortcanvas_gen")
#SequenceGenerator(name = "sortcanvas_gen", sequenceName = "sortcanvas_seq")
private int sortCanvasId;
Seems that your User class not implementing UserDetails Morever it might not be having proper return type of the getID method

passing in sql.oracle.ARRAY SimpleJdbcCall to StoredProcedure

I am using JBoss 7.1.Final as application server and Oracle as database. We are using Spring framework 3.x and Java 6. trying to pass in an array of Strings and convert them inside the stored proc to array of varchars. I haven't found a good example for this yet. Please provide a pointer if you can to any documentation or previous forum post. I have searched and not found one that seems to apply.
The stored proc is defined as:
CREATE OR REPLACE PROCEDURE GET_TEST_CONTENTS
(IN_RR_ARRAY IN RR_ARRAY,
IN_ORDER_STATE IN VARCHAR2,
OUT_FLAG OUT VARCHAR2,
OUT_RETURN_CODE OUT VARCHAR2,
OUT_RETURN_DESC OUT VARCHAR2,
OUT_RETURN_TYPE OUT VARCHAR2,
OUT_RETURN_VAL OUT NUMBER
)
Type RR_ARRAY is defined as:
create or replace
type RR_ARRAY as table of varchar2(15);
Within my java code I have:
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.getTestContents = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("STAR")
.withoutProcedureColumnMetaDataAccess()
.withProcedureName("GET_TEST_CONTENTS")
.declareParameters(
new SqlParameter("IN_RR_ARRAY", OracleTypes.ARRAY,
"RR_ARRAY"),
new SqlParameter("IN_ORDER_STATE", OracleTypes.VARCHAR), new SqlOutParameter("OUT_FLAG",
OracleTypes.VARCHAR),
new SqlOutParameter("OUT_RETURN_VAL", OracleTypes.INTEGER),
new SqlOutParameter("OUT_RETURN_CODE", OracleTypes.VARCHAR),
new SqlOutParameter("OUT_RETURN_DESC", OracleTypes.VARCHAR),
new SqlOutParameter("OUT_RETURN_TYPE", OracleTypes.VARCHAR));
//I get a different error here so creating new connection for testing
//conn = jdbcTemplate.getDataSource().getConnection();
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
conn = DriverManager.getConnection(jdbcURL, user, passwd);
ArrayDescriptor desc = new ArrayDescriptor("STAR.RR_ARRAY", conn);
ARRAY arr = new ARRAY(desc, conn, testArray); // testArray is just
// String[] with 2 values
Map<String, Object> hm = new HashMap<String, Object>();
hm.put("IN_RR_ARRAY", arr);
hm.put("IN_ORDER_STATE", stateCode);
hm.put("OUT_FLAG", Types.VARCHAR);
hm.put("OUT_RETURN_CODE", Types.VARCHAR);
hm.put("OUT_RETURN_DESC", Types.VARCHAR);
hm.put("OUT_RETURN_TYPE", Types.VARCHAR);
SqlParameterSource in = new MapSqlParameterSource().addValues(hm);
Map out = getTestContents .execute(in);
The stack trace returned is:
11:24:43,691 ERROR [com.test.repository.TestContentsDao] (http-localhost-127.0.0.1-8080-1) Error while calling GET_TEST_CONTENTS Stored procedure: org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call STAR.GET_TEST_CONTENTS(?, ?, ?, ?, ?, ?, ?)}]; SQL state [99999]; error code [17059]; Fail to convert to internal representation: oracle.sql.ARRAY#2a081f8f; nested exception is java.sql.SQLException: Fail to convert to internal representation: oracle.sql.ARRAY#2a081f8f
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:83) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:969) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1003) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:388) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:351) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:181) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at com.test.repository.TestContentsDao.isGood(TestContentsDao.java:147) [classes:]
Any advice or pointers to examples or docs will be appreciated
I found the fix for this. Now I use this List of Strings:
List<String> ndcList;
I changed the array parameter from OracleTypes.ARRAY to java.sql.stypes.ARRAY and specified schema prefix on array name. And changed the code and created a few new convenience methods at the bottom.
I needed the wrapped connection and had to add this dependency to my pom:
<dependency>
<groupId>jboss</groupId>
<artifactId>jboss-common-jdbc-wrapper</artifactId>
<version>3.2.3</version>
</dependency>
---- method code starts here------------
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.getTestContents = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("STAR")
.withoutProcedureColumnMetaDataAccess()
.withProcedureName("GET_TEST_CONTENTS")
.declareParameters(
new SqlParameter("IN_RR_ARRAY", java.sql.types.ARRAY, "STAR.RR_ARRAY"),
new SqlParameter("IN_ORDER_STATE", OracleTypes.VARCHAR),
new SqlOutParameter("OUT_FLAG", OracleTypes.VARCHAR),
new SqlOutParameter("OUT_RETURN_VAL", OracleTypes.INTEGER),
new SqlOutParameter("OUT_RETURN_CODE", OracleTypes.VARCHAR),
new SqlOutParameter("OUT_RETURN_DESC", OracleTypes.VARCHAR),
new SqlOutParameter("OUT_RETURN_TYPE", OracleTypes.VARCHAR));
Map<String, Object> hm = new HashMap<String, Object>();
hm.put("IN_RR_ARRAY", new ScriptArray(ndcList));
hm.put("IN_ORDER_STATE", stateCode);
hm.put("OUT_FLAG", Types.VARCHAR);
hm.put("OUT_RETURN_CODE", Types.VARCHAR);
hm.put("OUT_RETURN_DESC", Types.VARCHAR);
hm.put("OUT_RETURN_TYPE", Types.VARCHAR);
SqlParameterSource in = new MapSqlParameterSource().addValues(hm);
Map out = getTestContents.execute(in);
---- method code ends here------------
public class ScriptArray extends AbstractSqlTypeValue {
private List<String> values;
public ScriptArray(List<String> values) {
this.values = values;
}
public Object createTypeValue(Connection con, int sqlType,
String typeName) throws SQLException {
oracle.jdbc.OracleConnection wrappedConnection = con
.unwrap(oracle.jdbc.OracleConnection.class);
con = wrappedConnection;
ArrayDescriptor desc = new ArrayDescriptor(typeName, con);
return new ARRAY(desc, con,
(String[]) values.toArray(new String[values.size()]));
}
}
Been fighting the similar issue for a day. This article helped me.
Here's code backup, in case page will be unavailable:
-- custom type
create or replace TYPE "MY_TYPE"
as object(name varchar(255),
value varchar(255))
-- array of MY_TYPE
create or replace
TYPE "MY_ARRAY"
as table of MY_TYPE
-- echo like SP, doesn't do too much
create or replace
procedure foo(
i_array in MY_ARRAY,
o_array out MY_ARRAY)
as
begin
o_array := MY_ARRAY();
for i in 1 .. i_array.count loop
o_array.extend;
o_array(i) := MY_TYPE(i_array(i).name, i_array(i).value);
end loop;
end;
Java code:
public class FooStoredProcedure {
private static final String SP_NAME = "FOO";
private static final String MY_ARRAY = "MY_ARRAY";
private static final String MY_TYPE = "MY_TYPE";
private static final String I_ARRAY = "i_array";
private static final String O_ARRAY = "o_array";
private final StoredProcedure storedProcedure;
public FooStoredProcedure(DataSource dataSource) {
storedProcedure = new StoredProcedure(dataSource, SP_NAME) {
{
declareParameter(new SqlParameter(I_ARRAY, Types.ARRAY, MY_ARRAY));
declareParameter(new SqlOutParameter(O_ARRAY, Types.ARRAY, MY_ARRAY, new SqlReturnType() {
#Override
public Object getTypeValue(CallableStatement cs, int paramIndex,
int sqlType, String typeName) throws SQLException {
Connection connection = cs.getConnection();
Map<String, Class<?>> typeMap = connection.getTypeMap();
typeMap.put(MY_TYPE, MyType.class);
return cs.getObject(paramIndex);
}
}));
compile();
}
};
}
/**
* #return array of {#link MyType} objects or <code>null</code>
*/
public MyType[] execute(final MyType[] values) {
Map<String, Object> params = new HashMap<String, Object>();
params.put(I_ARRAY, new AbstractSqlTypeValue() {
#Override
protected Object createTypeValue(Connection con, int sqlType, String typeName) throws SQLException {
ArrayDescriptor descriptor = new ArrayDescriptor(typeName, con);
return new ARRAY(descriptor, con, values);
}
});
Map<?, ?> result = storedProcedure.execute(params);
if ((!result.containsKey(O_ARRAY) || result.get(O_ARRAY) == null)) {
return null;
}
try {
Object[] resultArray = (Object[]) ((ARRAY) result.get(O_ARRAY)).getArray();
return Arrays.copyOf(resultArray, resultArray.length, MyType[].class);
} catch (SQLException e) {
throw new DataRetrievalFailureException("Unable to retrieve array", e);
}
}
public static class MyType implements SQLData {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String getSQLTypeName() throws SQLException {
return MY_TYPE;
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
name = stream.readString();
value = stream.readString();
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(name);
stream.writeString(value);
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
}
I looked at the internet and it was very difficult to get it working with the solution many people have provided.. here is working code example.. in pom.xml create this dependency.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-oracle</artifactId>
<version>2.0.0.M1</version>
</dependency>
oracle sample code
create table employee (EMPNO number(12) not null, FNAME varchar2(60), LNAME varchar2(60), EMAIL varchar2(120));
CREATE SEQUENCE empno_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE OR REPLACE TYPE employee_type
AS OBJECT (EMPNO number(12), FNAME varchar2(60), LNAME varchar2(60), EMAIL varchar2(120));
/
CREATE OR REPLACE TYPE employee_table_type AS TABLE OF employee_type;
/
create or replace PROCEDURE SAVE_EMPLOYEES(p_emp_insert_array in employee_table_type) AS
BEGIN
FORALL i IN p_emp_insert_array.first .. p_emp_insert_array.last
insert into employee(
empno,
FNAME,
LNAME,
EMAIL)
values (
empno_seq.nextval,
p_emp_insert_array(i).FNAME,
p_emp_insert_array(i).LNAME,
p_emp_insert_array(i).EMAIL
);
END SAVE_EMPLOYEES;
/
import com.abc.employeepoc.domain.Employee;
import org.springframework.data.jdbc.support.oracle.StructMapper;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Struct;
/**
*
* #author rsharma
*/
public class EmployeeStructMapper implements StructMapper<Employee> {
#Override
public Struct toStruct(Employee emp, Connection conn, String oracleTypeName) throws SQLException {
Object[] attributes = {
emp.getEmpno(),
emp.getFirstName(),
emp.getLastName(),
emp.getEmailAddress()
};
return conn.createStruct(oracleTypeName, attributes);
}
#Override
public Employee fromStruct(Struct struct) throws SQLException {
Employee emp= new Employee();
Object[] attributes = struct.getAttributes();
emp.setEmpno(((Number) attributes[0]).longValue());
emp.setFirstName(String.valueOf(attributes[1]));
emp.setLastName(String.valueOf(attributes[2]));
emp.setEmailAddress(String.valueOf(attributes[3]));
return emp;
}
}
SqlStructArrayValue in spring has some issue with OracleConnection casting so i created my own one similar to them.
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Struct;
import oracle.jdbc.OracleConnection;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.jdbc.support.oracle.SqlStructArrayValue;
import org.springframework.data.jdbc.support.oracle.StructMapper;
/**
*
* #author rsharma
*/
public class OracleSqlStructArrayValue<T> extends SqlStructArrayValue<T> {
private T[] values;
/**
* The object that will do the mapping *
*/
private StructMapper<T> mapper;
/**
* The type name of the STRUCT *
*/
private String structTypeName;
/**
* The type name of the ARRAY *
*/
private String arrayTypeName;
public OracleSqlStructArrayValue(T[] values, StructMapper<T> mapper, String structTypeName) {
super(values, mapper, structTypeName);
this.values = values;
this.mapper = mapper;
this.structTypeName = structTypeName;
}
public OracleSqlStructArrayValue(T[] values, StructMapper<T> mapper, String structTypeName, String arrayTypeName) {
super(values, mapper, structTypeName, arrayTypeName);
this.values = values;
this.mapper = mapper;
this.structTypeName = structTypeName;
this.arrayTypeName = arrayTypeName;
}
#Override
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
if (typeName == null && arrayTypeName == null) {
throw new InvalidDataAccessApiUsageException(
"The typeName for the array is null in this context. Consider setting the arrayTypeName.");
}
Struct[] structValues = new Struct[values.length];
for (int i = 0; i < values.length; i++) {
structValues[i] = mapper.toStruct(values[i], conn, structTypeName);
}
OracleConnection oracleConn = (OracleConnection) conn;
return oracleConn.createOracleArray(typeName != null ? typeName : arrayTypeName, structValues);
}
}
Now in your DAO Class do the following...
public class EmployeeDAO {
private static final Logger logger = LoggerFactory.getLogger(EmployeeDAO.class);
#Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall saveEmployeesArrayCall;
#PostConstruct
private void postConstruct() {
jdbcTemplate = new JdbcTemplate(dataSource);
this.saveEmployeesArrayCall =
new SimpleJdbcCall(dataSource).withProcedureName(SQLConstants.SAVE_EMPLOYEES_STORE_PROC)
.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter("p_emp_insert_array", Types.ARRAY, SQLConstants.EMPLOYEE_OBJ_TABLE_TYPE));
}
public void saveEmployees(List<Employee> employees) {
Map<String, Object> in = new HashMap<>();
in.put("p_emp_insert_array", new OracleSqlStructArrayValue<>(employees.toArray(new Employee[0]), new EmployeeStructMapper(), SQLConstants.EMPLOYEE_OBJ_TYPE));
saveEmployeesArrayCall.execute(in);
}
}
import io.swagger.annotations.ApiModelProperty;
import java.util.Objects;
import org.springframework.data.annotation.Id;
/**
*
* #author rsharma
*/
public class Employee implements java.io.Serializable{
#Id
#ApiModelProperty(notes = "The database generated Employee Number")
private Long empno;
#ApiModelProperty(notes = "First Name of the Employee", required = true)
private String firstName;
#ApiModelProperty(notes = "Last Name of the Employee")
private String lastName;
private String emailAddress;
public Employee() {
super();
}
public Employee(Long empno, String emailAddress, String firstName, String lastName) {
this.empno = empno;
this.emailAddress = emailAddress;
this.firstName = firstName;
this.lastName = lastName;
}
public Long getEmpno() {
return empno;
}
public void setEmpno(Long empno) {
this.empno = empno;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
#Override
public String toString() {
return "Employee{" + "empno=" + empno + ", firstName=" + firstName + ", lastName=" + lastName + ", emailAddress=" + emailAddress + '}';
}
#Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + Objects.hashCode(this.empno);
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Employee other = (Employee) obj;
if (!Objects.equals(this.empno, other.empno)) {
return false;
}
return true;
}
}

Resources