Oracle char type issue in Hibernate HQL query - oracle

I have Oracle table, which contains char type columns. In my Entity class i mapped oracle char type to java string type.
Here is the code for my Entity class.
#Entity
#Table(name="ORG")
public class Organization {
private String serviceName;
private String orgAcct;
//Some other properties goes here...
#Column(name="ORG_ACCT", nullable=false, length=16)
public String getOrgAcct() {
return this.orgAcct;
}
public void setOrgAcct(String orgAcct) {
this.orgAcct = orgAcct;
}
#Column(name="SERVICE_NAME",nullable=true, length=16)
public String getServiceName() {
return this.serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
}
Here both serviceName and orgAcct are char type variables in Oracle
In my DAO class I wrote a HQL query to fetch Oranization entity object using serviceName and orgAcct properties.
#Repository
#Scope("singleton") //By default scope is singleton
public class OrganizationDAOImpl implementsOrganizationDAO {
public OrganizationDAOImpl(){
}
public Organization findOrganizationByOrgAcctAndServiceName(String orgAcct,String serviceName){
String hqlQuery = "SELECT org FROM Organization org WHERE org.serviceName = :serName AND org.orgAcct = :orgAct";
Query query = getCurrentSession().createQuery(hqlQuery)
.setString("serName", serviceName)
.setString("orgAct", orgAcct);
Organization org = findObject(query);
return org;
}
}
But when I call findOrganizationByOrgAcctAndServiceName() method , I am getting Organization object as null(i.e. HQL query is not retrieving Char type data ).
Please help me to fix this issue. Here I can't change Oracle type char to Varchar2. I need to work with oracle char type variables.
#EngineerDollery After going throw above post, I modified my Entity class with columnDefinition , #Column annotation attribute.
#Column(name="SERVICE_NAME",nullable=true,length=16,columnDefinition="CHAR")
public String getServiceName() {
return this.serviceName;
}
But still I am not able to retrieve the data for corresponding columns.
and I added column size as well in columnDefinition attribute.
#Column(name="SERVICE_NAME",nullable=true,length=16,columnDefinition="CHAR(16)
But still same issue I am facing.
Any thing Am I doing wrong. Please help me.

I resolved this problem using OraclePreparedStatement and Hibernate UserType interface.
Crated a new UserType class by extending org.hibernate.usertype.UserType interface and provided implementation for nullSafeSet(), nullSafeGet() methods .
nullSafeSet() method, we have first parameter as PreparedStatement, inside the method I casted PreparedStatement into OraclePreparedStatement object and pass String value using setFixedCHAR() method.
Here is the complete code of UserType impl class.
package nc3.jws.persistence.userType;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.apache.commons.lang.StringUtils;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
/**
*
* based on www.hibernate.org/388.html
*/
public class OracleFixedLengthCharType implements UserType {
public OracleFixedLengthCharType() {
System.out.println("OracleFixedLengthCharType constructor");
}
public int[] sqlTypes() {
return new int[] { Types.CHAR };
}
public Class<String> returnedClass() {
return String.class;
}
public boolean equals(Object x, Object y) {
return (x == y) || (x != null && y != null && (x.equals(y)));
}
#SuppressWarnings("deprecation")
public Object nullSafeGet(ResultSet inResultSet, String[] names, Object o) throws SQLException {
//String val = (String) Hibernate.STRING.nullSafeGet(inResultSet, names[0]);
String val = StringType.INSTANCE.nullSafeGet(inResultSet, names[0]);
//System.out.println("From nullSafeGet method valu is "+val);
return val == null ? null : StringUtils.trim(val);
}
public void nullSafeSet(PreparedStatement inPreparedStatement, Object o,
int i)
throws SQLException {
String val = (String) o;
//Get the delegatingStmt object from DBCP connection pool PreparedStatement object.
org.apache.commons.dbcp.DelegatingStatement delgatingStmt = (org.apache.commons.dbcp.DelegatingStatement)inPreparedStatement;
//Get OraclePreparedStatement object using deletatingStatement object.
oracle.jdbc.driver.OraclePreparedStatement oraclePreparedStmpt = (oracle.jdbc.driver.OraclePreparedStatement)delgatingStmt.getInnermostDelegate();
//Call setFixedCHAR method, by passing string type value .
oraclePreparedStmpt.setFixedCHAR(i, val);
}
public Object deepCopy(Object o) {
if (o == null) {
return null;
}
return new String(((String) o));
}
public boolean isMutable() {
return false;
}
public Object assemble(Serializable cached, Object owner) {
return cached;
}
public Serializable disassemble(Object value) {
return (Serializable) value;
}
public Object replace(Object original, Object target, Object owner) {
return original;
}
public int hashCode(Object obj) {
return obj.hashCode();
}
}
Configured this class with #TypeDefs annotation in Entity class.
#TypeDefs({
#TypeDef(name = "fixedLengthChar", typeClass = nc3.jws.persistence.userType.OracleFixedLengthCharType.class)
})
Added this type to CHAR type columns
#Type(type="fixedLengthChar")
#Column(name="SERVICE_NAME",nullable=true,length=16)
public String getServiceName() {
return this.serviceName;
}

char types are padded with spaces in the table. This means that if you have
foo
in one of these columns, what you actually have is
foo<space><space><space>...
until the actual length of the string is 16.
Consequently, if you're looking for an organization having "foo" as its service name, you won't find any, because the actual value in the table if foo padded with 13 spaces.
You'll thus have to make sure all your query parameters are also padded with spaces.

Related

How do I insert values of elements that are part of the EmbeddedId in JPA?

I have a case where I need to execute an insert statement via createNativeQuery. I have an entity list I'm looping through in order to set the properties accordingly from another bean class, and then persist that data to the oracle database.
The problem I am facing is persisting the data that is part of the embeddedId (item, loc, weekstart, type, forecastId, insertTS). I need to persist that data for the new records to be inserted into the database. When I try to set the values from the POJO bean to my set method for the properties of my entity bean, nothing happens. Below is my code for setting the values of the properties from the POJO bean to my entity bean, along with my persistence method and the insert query being executed:
Validation class where validation occurs beforehand (missing to get the point) that includes the setting of my entity properties from the POJO bean:
List <InsertPromoData> insertPromos = new ArrayList<InsertPromoData>();
promo.forEach(record -> {
if (record.getErrorList().size() == 0) {
rowsSuccessful++;
Util.writeSuccessToFile(templateCd, successFile, record, successFields);
try {
InsertPromoData insertData = new InsertPromoData();
insertData.getId().setItem(record.getItem());
insertData.getId().setLoc(record.getLoc());
insertData.getId().setWeekStart(record.getWeek_Start_Date());
insertData.setNumberOfWeeks(record.getNumber_Of_Weeks());
insertData.getId().setType(record.getType());
insertData.getId().setForecastId(record.getForecast_ID());
insertData.setQty(record.getUnits());
insertPromos.add(insertData);
}
catch (Exception e) {
logger.error("Error with setting insertPromolist from promo list values and the error is " + e.getMessage());
}
}
else {
if (rowsFailure == 0) {
Util.writeHeaderToFile(templateCd, errorFile);
}
rowsFailure++;
Util.writeErrorToFile(templateCd, errorFile, record, record.getErrorList());
}
});
errorFile.close();
successFile.close();
OracleImpl.insertPromoData(insertPromos);
POJO bean (promo is the variable representing this list of beans in validation class above):
public class PromoBean extends ErrorListBean
{
public String Item;
public String Loc;
public String Week_Start_Date;
public String Units;
public String Forecast_ID;
public String Type;
public String Number_Of_Weeks;
public String getItem() {
return Item;
}
public void setItem(String item) {
Item = item;
}
public String getLoc() {
return Loc;
}
public void setLoc(String loc) {
Loc = loc;
}
public String getWeek_Start_Date() {
return Week_Start_Date;
}
public void setWeek_Start_Date(String week_Start_Date) {
Week_Start_Date = week_Start_Date;
}
public String getNumber_Of_Weeks() {
return Number_Of_Weeks;
}
public void setNumber_Of_Weeks(String number_Of_Weeks) {
Number_Of_Weeks = number_Of_Weeks;
}
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
public String getForecast_ID() {
return Forecast_ID;
}
public void setForecast_ID(String forecast_ID) {
Forecast_ID = forecast_ID;
}
public String getUnits() {
return Units;
}
public void setUnits(String units) {
Units = units;
}
}
Embeddable class representing the composite primary key of the table:
#Embeddable
public class PromoID implements Serializable {
#Column(name = "ITEM")
private String item;
#Column(name = "LOC")
private String loc;
#Column(name = "WK_START")
private String weekStart;
#Column(name = "TYPE")
private String type;
#Column(name = "FCSTID")
private String forecastId;
#Column(name = "U_TIMESTAMP")
private String insertTS;
public PromoID() {
}
public PromoID (String item, String loc, String weekStart, String type, String forecastId, String insertTS) {
this.item = item;
this.loc = loc;
this.weekStart = weekStart;
this.type = type;
this.forecastId = forecastId;
this.insertTS = insertTS;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getWeekStart() {
return weekStart;
}
public void setWeekStart(String weekStart) {
this.weekStart = weekStart;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getForecastId() {
return forecastId;
}
public void setForecastId(String forecastId) {
this.forecastId = forecastId;
}
public String getInsertTS() {
return insertTS;
}
public void setInsertTS(String insertTS) {
this.insertTS = insertTS;
}
//hashcode and equals methods
Persistence Bean:
#Entity
#Table(name = "U_USER_PROMO")
public class InsertPromoData {
#EmbeddedId
private PromoID id;
#Column(name="NUMBER_OF_WEEKS")
String numberOfWeeks;
#Column(name="QTY")
String qty;
#Id
#AttributeOverrides(
{
#AttributeOverride(name = "item",column = #Column(name="ITEM")),
#AttributeOverride(name = "loc", column = #Column(name="LOC")),
#AttributeOverride(name = "weekStart", column = #Column(name="WK_START")),
#AttributeOverride(name = "type", column = #Column(name="TYPE")),
#AttributeOverride(name = "forecastId", column = #Column(name="FCSTID"))
}
)
public PromoID getId() {
return id;
}
public void setId(PromoID id) {
this.id = id;
}
public String getNumberOfWeeks() {
return numberOfWeeks;
}
public void setNumberOfWeeks(String numberOfWeeks) {
this.numberOfWeeks = numberOfWeeks;
}
public String getQty() {
return qty;
}
public void setQty(String qty) {
this.qty = qty;
}
}
DAO class method to execute the update (entitymanagerfactory emf already initialized):
public static void insertPromoData(List<InsertPromoData> insertData) {
logger.debug("Execution of method insertPromoData in Dao started");
System.out.println("Size of the insertData list is " + insertData.size());
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
System.out.println("Beginning transaction for insertPromoData");
Query query = em.createNativeQuery(env.getProperty("insertPromoUploadData"));
for (InsertPromoData promoData : insertData) {
query.setParameter("item", promoData.getId().getItem());
query.setParameter("location", promoData.getId().getLoc());
query.setParameter("wkStart", promoData.getId().getWeekStart());
query.setParameter("numberOfWeeks", promoData.getNumberOfWeeks());
query.setParameter("type", promoData.getId().getType());
query.setParameter("fcstId", promoData.getId().getForecastId());
query.setParameter("quantity", promoData.getQty());
query.executeUpdate();
}
em.getTransaction().commit();
}
catch(Exception e) {
logger.error("Exception in beginning transaction");
e.printStackTrace();
}
finally {
em.clear();
em.close();
}
logger.debug("Execution of method insertPromoData in Dao ended");
}
Query in properties file:
insertPromoUploadData = INSERT INTO {h-schema}U_USER_PROMO (ITEM, LOC, WK_START, NUMBER_OF_WEEKS, TYPE, FCSTID, QTY, U_TIMESTAMP) VALUES (:item, :location, TO_DATE(:wkStart,'MM DD YYYY'), :numberOfWeeks, :type, :fcstId, :quantity, SYSDATE)
My list size from my DAO class is returning as 0 once I begin the transaction and not sure why it is empty. Is there a reason that it is empty? I'm trying to persist each of the fields to the database (including the composite key fields) via insert query. Any help appreciated.
After looking into this for hours, I finally came to the conclusion that the simplest way to executeUpdate() without running into issues due to my current #EmbeddedId/#Embeddable logic was to change it to use #IdClass for my composite PK class, and annotate the fields from the PK in my entity with #Id. This allowed my data to be persisted to the database. Another slight difference was adding the insertTS in my entity class and annotating with #Id and generating getters/setters. This was necessary for JPA to recognize all the properties being referenced that I am wanting to persist, though I am persisting insertTS using SYSDATE function from the oracle DB instead of utilizing the get/set methods and setting to the current time from the java side.
I am sure there is a way to use #EmbeddedId/#Embeddable logic and be able to persist the fields that are part of the EmbeddedId, however, this I found to be a more simplistic way of doing it without further complexity in the code.

Entity Data Load _in Spring Batch Boot

Am using Spring Boot + Spring Batch + myBatis. we have 2 entities (DepartmentEntity, EmpoyeeEntity) / Department-mapper.xml and Employee-mapper.xml
EmpValidationEntity.java(Employee Entity)
public class EmpValidationEntity{
private empName;
private empDept;
setter/getter
}
DepartmentValidationEntity.java(Department Entity)
public class DeptValidationEntity{
private deptName;
private Dept;
setter/getter
}
EmpMapper.xml(Employee mapper file-myBatis based)
//select query from Emp
DeptMapper.xml(Department mapper file)
//select query from Dept
EmpInterDependentFieldsValidator.java (Validator class file)
public class EmpInterDependentFieldsValidator
implements ConstraintValidator<EmpInterDependentFieldsValidation, EmpValidationEntity> {
#Override
public boolean isValid(SSNValidationEntity value, ConstraintValidatorContext context) {
Object fieldValue = null;
boolean isValid = false;
boolean isFieldValid = true;
try {
fieldValue = BeanUtils.getProperty(value, fieldName);
}
catch(Exception e){}
if (isFieldValid) {
isValid = validateFields(value, context, fieldValue, fieldName, isValid);
} else {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(ErrorMessageConstant.INVALID_FIELD)
.addPropertyNode(fieldName).addConstraintViolation();
}
}
return isValid;}
private boolean validateFields(EmpValidationEntity value, ConstraintValidatorContext context, Object fieldToValid,
String fieldName, boolean isValid) {
String fieldValue = null;
if (fieldToValid != null) {
fieldValue = fieldToValid.toString();
}
if (fieldName.equalsIgnoreCase("EMPLOYEE_DEPT")) {
return EmpValidationService.validate(fieldName, fieldValue, value, context);
}
return isValid;
}
}
EmpValidationService.java(validation service class)
public class EmpValidationService{
static List<String> ssnList=null;
public static boolean validate(String fieldName, String fieldValue, EmpValidationEntity entity, ConstraintValidatorContext context) {
StringBuilder errorMessage = new StringBuilder();
boolean isValid = true;
if(departmentList.contains(entity.getEmpdept())){
errorMessage.append(ErrorMessageConstant.SSN_ALREADY_TIED_TO_ANOTHER_DOB);}
}
In above class how to get DepartmentList (in single load/dump)?
from the above code ,i want pass two entity and get the scope of two entity object data into EmpValidationService class, after that i want store each entity data into different list because i want check one entity data contains to another entity data.

JPA repository fail with good name of property and works with wrong name

I have a class with property named isChecked with is boolean type. In Jpa repository I wrote a method to find all rows which has isChecked = false;
public interface ReservationReminderRepository extends JpaRepository<ReservationReminder, Integer> {
ReservationReminder findByReservationReminderId(Integer id);
//#Query("select r from ReservationReminder r where r.isChecked = :checked")
List<ReservationReminder> findByChecked(boolean checked);
}
While I tried to call a method findByChecked() in Jpa reporistory everything works, but when I tried to run a method with the proper named of property - as it is in jpa doc findByIsChecked() I got strange fails:
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [isChecked] on this ManagedType [com.carwash.domains.ReservationReminder]
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.checkNotNull(AbstractManagedType.java:128)
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.getAttribute(AbstractManagedType.java:113)
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:566)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:334)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:277)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:182)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:109)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:49)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:118)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:241)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:68)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103)
... 104 more
Can anyone tell me why I received that kind of fail? How the method name would looks like when I'd like to check with property checkedDate?
package com.carwash.domains;
import javax.persistence.*;
import java.util.Date;
/**
* Created by mbi on 01.03.2017.
*/
#Entity
public class ReservationReminder {
private int reservationReminderId;
private Reservation reservation;
private boolean isChecked;
private Date checkedDate;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getReservationReminderId() {
return reservationReminderId;
}
public void setReservationReminderId(int reservationReminderId) {
this.reservationReminderId = reservationReminderId;
}
#OneToOne(mappedBy = "reservationReminder")
public Reservation getReservation() {
return reservation;
}
public void setReservation(Reservation reservation) {
this.reservation = reservation;
}
public boolean getChecked() {
return isChecked;
}
public void setChecked(Boolean checked) {
isChecked = checked;
}
public Date getCheckedDate() {
return checkedDate;
}
public void setCheckedDate(Date checkedDate) {
this.checkedDate = checkedDate;
}
#Override
public String toString() {
return "ReservationReminder{" +
"reviewId=" + reservationReminderId +
", isChecked=" + isChecked +
", checkedDate=" + checkedDate +
'}';
}
public ReservationReminder() {
}
public ReservationReminder(Boolean isChecked, Date checkedDate) {
this.isChecked = isChecked;
this.checkedDate = checkedDate;
}
public ReservationReminder(int reservationReminderId, Reservation reservation, boolean isChecked, Date checkedDate) {
this.reservationReminderId = reservationReminderId;
this.reservation = reservation;
this.isChecked = isChecked;
this.checkedDate = checkedDate;
}
}
It seems that the problem is related to the naming of that property.
As you are telling Spring to look for findByChecked and the property name is isChecked.
You can try to use findByIsChecked and change the getter to isChecked.
But actually i would change the property to checked, getter to isChecked and leave the jpa query method as it is.

How to convert Oracle user defined Type into java object in spring jdbc stored procedure

I am working on springjdbcTemplate, and all db call will be done through stored procedures. In Oracle 11g I have created one user defined type containing with other type as field inside it as below.
create or replace type WORKER AS Object (NAME VARCHAR2(30),
age NUMBER);
create or replace type WORKER_LIST IS TABLE OF WORKER;
create or replace type MANAGER AS Object(
NAME VARCHAR2(30),
workers WORKER_LIST
);
And at Java side I have created the classes as follows.
public class Worker implements SQLData {
private String name;
private int age;
#Override
public String getSQLTypeName() throws SQLException {
return "WORKER";
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
setAge(stream.readInt());
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getName());
stream.writeInt(getAge());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Manager implements SQLData {
private String name;
private List<Worker> workers;
#Override
public String getSQLTypeName() throws SQLException {
return "Manager";
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
setWorkers((List<Worker>) stream.readObject());
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getName());
stream.writeObject((SQLData) getWorkers());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Worker> getWorkers() {
return workers;
}
public void setWorkers(List<Worker> workers) {
this.workers = workers;
}
}
I have mentioned in typeMap about the mappings.
But I am not getting expected results.
Worker type is returned as Struct and List<Worker> is returned as array.
Please let me know what should I have do and what is the standard protocol to get the expected object as I mentioned above. I'm new to JDBCTemplate. Please suggest.
Thanks
Ram
I think I've managed to get something working.
You mentioned something about the connection's type map. When using Spring it's difficult to get hold of the database connection in order to add the types to the connection's type map, so I'm not sure what you mean when you write 'I have mentioned in typeMap about the mappings'.
Spring offers one way to add an entry to the connection's type map, in the form of the class SqlReturnSqlData. This can be used to call a stored procedure or function which returns a user-defined type. It adds an entry to the connection's type map to specify the database type of the object and the class to map this object to just before it retrieves a value from a CallableStatement. However, this only works if you only need to map a single type. You have two such types that need mapping: MANAGER and WORKER.
Fortunately, it's not difficult to come up with a replacement for SqlReturnSqlData that can add more than one entry to the connection's type map:
import org.springframework.jdbc.core.SqlReturnType;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
public class SqlReturnSqlDataWithAuxiliaryTypes implements SqlReturnType {
private Class<?> targetClass;
private Map<String, Class<?>> auxiliaryTypes;
public SqlReturnSqlDataWithAuxiliaryTypes(Class<?> targetClass, Map<String, Class<?>> auxiliaryTypes) {
this.targetClass = targetClass;
this.auxiliaryTypes = auxiliaryTypes;
}
#Override
public Object getTypeValue(CallableStatement cs, int paramIndex, int sqlType, String typeName) throws SQLException {
Connection con = cs.getConnection();
Map<String, Class<?>> typeMap = con.getTypeMap();
typeMap.put(typeName, this.targetClass);
typeMap.putAll(auxiliaryTypes);
Object o = cs.getObject(paramIndex);
return o;
}
}
The above has been adapted from the source of SqlReturnSqlData. All I've really done is added an extra field auxiliaryTypes, the contents of which gets added into the connection's type map in the call to getTypeValue().
I also needed to adjust the readSQL method of your Manager class. The object you read back from the stream will be an implementation of java.sql.Array. You can't just cast this to a list. Sadly, getting this out is a little fiddly:
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
Array array = (Array) stream.readObject();
Object[] objects = (Object[]) array.getArray();
List<Worker> workers = Arrays.stream(objects).map(o -> (Worker)o).collect(toList());
setWorkers(workers);
}
(If you're not using Java 8, replace the line with Arrays.stream(...) with a loop.)
To test this I wrote a short stored function to return a MANAGER object:
CREATE OR REPLACE FUNCTION f_get_manager
RETURN manager
AS
BEGIN
RETURN manager('Big Boss Man', worker_list(worker('Bill', 40), worker('Fred', 36)));
END;
/
The code to call this stored function was then as follows:
Map<String, Class<?>> auxiliaryTypes = Collections.singletonMap("WORKER", Worker.class);
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("my_schema")
.withFunctionName("f_get_manager")
.declareParameters(
new SqlOutParameter(
"return",
OracleTypes.STRUCT,
"MANAGER",
new SqlReturnSqlDataWithAuxiliaryTypes(Manager.class, auxiliaryTypes)));
Manager manager = jdbcCall.executeFunction(Manager.class);
// ... do something with manager.
This worked, in that it returned a Manager object with two Workers in it.
Finally, if you have stored procedures that save a Manager object to the database, be aware that your Manager class's writeSQL method will not work. Unless you've written your own List implementation, List<Worker> cannot be casted to SQLData. Instead, you'll need to create an Oracle array object and put the entries in that. That however is awkward because you'll need the database connection to create the array, but that won't be available in the writeSQL method. See this question for one possible solution.

Hibernate CompositeUserType mapping has wrong number of columns

I am new to Hibernate. Writing a CompositeUserType. When I run the code I am getting error.
property
mapping has wrong number of columns:
Please help me what am I missing?
My CompositeUserType goes as follows
public class EncryptedAsStringType implements CompositeUserType {
#Override
public String[] getPropertyNames() {
return new String[] { "stockId", "stockCode", "stockName","stockDescription" };
}
#Override
public Type[] getPropertyTypes() {
//stockId, stockCode,stockName,modifiedDate
return new Type[] {
Hibernate.INTEGER, Hibernate.STRING, Hibernate.STRING,Hibernate.STRING
};
}
#Override
public Object getPropertyValue(final Object component, final int property)
throws HibernateException {
Object returnValue = null;
final Stock auditData = (Stock) component;
if (0 == property) {
returnValue = auditData.getStockId();
} else if (1 == property) {
returnValue = auditData.getStockCode();
} else if (2 == property) {
returnValue = auditData.getStockName();
} return returnValue;
}
#Override
public void setPropertyValue(final Object component, final int property,
final Object setValue) throws HibernateException {
final Stock auditData = (Stock) component;
}
#Override
public Object nullSafeGet(final ResultSet resultSet,
final String[] names,
final SessionImplementor paramSessionImplementor, final Object paramObject)
throws HibernateException, SQLException {
//owner here is of type TestUser or the actual owning Object
Stock auditData = null;
final Integer createdBy = resultSet.getInt(names[0]);
//Deferred check after first read
if (!resultSet.wasNull()) {
auditData = new Stock();
System.out.println(">>>>>>>>>>>>"+resultSet.getInt(names[1]));
System.out.println(">>>>>>>>>>>>"+resultSet.getString(names[2]));
System.out.println(">>>>>>>>>>>>"+resultSet.getString(names[3]));
System.out.println(">>>>>>>>>>>>"+resultSet.getString(names[4]));
}
return auditData;
}
#Override
public void nullSafeSet(final PreparedStatement preparedStatement,
final Object value, final int property,
final SessionImplementor sessionImplementor)
throws HibernateException, SQLException {
if (null == value) {
} else {
final Stock auditData = (Stock) value;
System.out.println("::::::::::::::::::::::::::::::::"+auditData.getStockCode());
System.out.println("::::::::::::::::::::::::::::::::"+auditData.getStockDescription());
System.out.println("::::::::::::::::::::::::::::::::"+auditData.getStockId());
System.out.println("::::::::::::::::::::::::::::::::"+auditData.getStatus());
}
}
My Domain class Stock has five attributes. (stockId,stockCode,StockName,Status , Stock
Description)
I need to declare the field Stock description as Composite field Type.
private Integer stockId;
private String stockCode;
private String stockName;
private String status;
private String stockDescription;
//Constructors
#Column(name = "STOCK_CC", unique = true, nullable = false, length = 20)
#Type(type="com.mycheck.EncryptedAsStringType")
#Columns(columns = { #Column(name="STOCK_ID"),
#Column(name="STOCK_CODE"),
#Column(name="STOCK_NAME")
})
public String getStockDescription() {
return stockDescription;
}
}
When I try to execute a insert for Stock. I am getting the error Error creating bean with name
'sessionFactory' defined in class path resource [spring/config/../database/Hibernate.xml]:
Invocation of init method failed. nested exception is org.hibernate.MappingException:
property mapping has wrong number of columns: com.stock.model.Stock.stockDescription type:
com.mycheck.EncryptedAsStringType
Where am I going wrong ?
One can extract the answer from the code samples and the comments to the original question, but to save everyone some reading, I've compiled a quick summary.
If you declare a CompositeUserType that maps a type to n columns, you have to declare n columns in #Columns besides the #Type annotation. Example:
public class EncryptedAsStringType implements CompositeUserType {
#Override
public String[] getPropertyNames() {
return new String[] { "stockId", "stockCode", "stockName","stockDescription" };
}
// ...
}
This CompositeUserType maps to 4 separate columns, therefore 4 separate #Column annotations have to be declared:
#Type(type="com.mycheck.EncryptedAsStringType")
#Columns(columns = {
#Column(name="STOCK_ID"),
#Column(name="STOCK_CODE"),
#Column(name="STOCK_NAME"),
#Column(name="STOCK_DESCRIPTION")
})
public String getStockDescription() {
return stockDescription;
}
That's it and Hibernate is happy.

Resources