Hibernate to execute multiple native sql statements - java

How can I execute Multiple SQL statements in a single sql query using hibernate native sql.
String sql = "SELECT * FROM user; SELECT * FROM product;";
UserVO valueObject = new UserVO();
databaseObject.select(sql, valueObject);
Database Object
public List select(String sql, Object valueObject) throws Exception {
Session session = Entitlement.getSessionFactory().openSession();
session.beginTransaction();
List list = session.createSQLQuery(sql).setProperties(valueObject).list();
session.close();
return list;
}

Use union to form a query which has same returning data
(Select EMPLOYEEID as id, EMPLOYEE_NAME as name, "EMPOYEE" as type) UNION (SELECT PRODUCTID as id, Product_NAME as name, "PRODUCT" as type)
form an Entity to hold it
class EntityDetail {
String id;
String name;
String type;
}
I have added an additional column value type to simply identify from which table row is coming from. And yes you will need to form a proper Entity with all valid annotations the above Entity is just for example.

just a lateral approach.
public List<List<Object[]>> execute(String sqls, Object valueObject) throws Exception {
String[] queries = sqls.split(";");
List<List<Object[]>> result = new ArrayList<>();
for(int i=0; i<queries.length; i++) {
result.add(this.select(queries[i], valueObject));
}
return result;
}

Related

Map Custom JdbcTemplate query result in an Object

I new in java and try to use spring framework. I have a question.
By example, I have table :
employee (id_employee, name)
employee_product (id_employee_product, id_employee, product_name)
if I select an employee data from my Employee table, I can map it in a POJO model User and define the tables structure in that model, like this:
public class Employee {
private final int id_employee;
private final String nama;
public Employee(int id_employee, String nama){
this.id_employee = id_employee;
this.nama = nama;
}
public int getId() {
return id_employee;
}
public String getNama() {
return nama;
}
}
And this is the map from jdbcTemplate:
final String sql = "SELECT id_employee, nama FROM employee";
return jdbcTemplate.query(sql, (resultSet, i) -> {
return new Employee(
resultSet.getInt("id_employee"),
resultSet.getString("nama")
);
});
That is clear example for select data from 1 table.
My question is, how to map data from query if my data is custom query? Such us using join and select custom field from that tables, Am I need to create POJO every query?
Sometimes I need to select only employee.id_employee, and employee.name field from my employee table.
And in another controller I need to select employee.id_employee from my employee table.
In another case, I need only select employee.name, and employee_product.product_name
Is there an alternative to map the data without creating POJO for every case?
Create a one POJO combining two tables like this
public class Employee {
private int id_employee;
private String name;
private int id_employee_product.
private String product_name
//getter and setters
//Don't create a constructor its Entiry
}
Now by using a BeanPropertyRowMapper Doc Link write your repository like
public List<Employee> fetchEmployeeProduct(){
JdbcTemplate jdbcTemplate = new JdbcTemplate("Your_DataSource");
StringBuilder query = new StringBuilder();
query.append("Your Query");
List<Employee> employeeProductList =
jdbcTemplate.query(query.toString(), new BeanPropertyRowMapper<Employee>(Employee.class));
}
Make sure SELECT clause in the query and Employee POJO's filed name is same.
Once if you execute your query it will automatically map to POJO. You no need to write a custom mapper BeanPropertyRowMapperwill take care of mapping.

How i can receive one object instead many using spring JdbcTemplate when i using inner join comand in sql?

I have a class User with fields:
private Long id;
private String u_name;
private String u_surname;
private int u_age;
private Set<Roles> roles = new HashSet<>(); //its enum
When i tried to display it in response using JdbcTemplate and rest controller I have a problem because i receive three object in it.
My repository classes:
public List<User> findAll() {
String sql = "select users.id, users.u_name, users.u_surname, users.u_age, user_roles.user_role from users inner join user_roles on users.id = user_roles.user_id";
return jdbc.query(sql, new RowMapper<User>(){
#Override
public User mapRow(ResultSet rs, int row) throws SQLException {
Set<Roles> roles = new HashSet<>();
roles.add(Roles.valueOf(rs.getString("user_role")));
return new User(rs.getLong("id"), rs.getString("u_name"), rs.getString("u_surname"), rs.getInt("u_age"), roles);
}
});
}
public User findById(Long id) {
String sql = "select users.id, users.u_name, users.u_surname, users.u_age, user_roles.user_role from users inner join user_roles on users.id = user_roles.user_id where users.id = ?";
return jdbc.queryForObject(sql, new RowMapper<User>(){
#Override
public User mapRow(ResultSet rs, int row) throws SQLException {
Set<Roles> roles = new HashSet<>();
roles.add(Roles.valueOf(rs.getString("user_role")));
return new User(rs.getLong("id"), rs.getString("u_name"), rs.getString("u_surname"), rs.getInt("u_age"), roles);
}
}, id);
}
What do I need to do to receive only one object instead many?
That happens because you probably have more then one role attached to your user, so your SQL will result in a matrix (more roles, more lines), to solve that you will have to remove the join, but only if you don't need to see roles. However, if you have to retrieve user that has roles, you can change your sql to use 'exists' like that:
select
users.id,
users.u_name,
users.u_surname,
users.u_age,
user_roles.user_role
from users
where exists (
select 1 from user_roles
where users.id = user_roles.user_id)
and users.id = ?
*haven't tested
If, after all, you really need to know the roles, i recommend you to create a separate method to retrieve them

JPA NAMED Query issue

Getting below database excpetion, help required
service() for servlet catalogservice threw exception: java.lang.IllegalArgumentException: Named query not found: SELECT OMX_PLAN_ID, PLAN_ID,DECODE(plan_id,0,ser_input_total_amount,first_payment) first_paymenmt From (SELECT OMX_PLAN_ID, PLAN_ID,(SELECT DECODE(fraction,0,fixed_payment_amount, (( fraction/100) * :useinput_total_amount)) From TFN.VW_OMX_PAYMENT_PLAN_DETAILS i WHERE o.OMX_PLAN_ID=i.OMX_PLAN_ID AND i.OMX_PLAN_ID=:omxPlanId AND i.PAYMENT_ID=1) first_payment FM TFN.VW_OMX_PAYMENT_PLANS o ) WHERE OMX_PLAN_ID=:omxPlanId ORDER by 1
at org.hibernate.ejb.AbstractEntityManagerImpl.createNamedQuery(AbstractEntityManagerImpl.java:704) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Fin
DAO Method
public TermPayment findFirstPaymentByTotalAndPlanId(int planId, double totalAmount) {
TypedQuery<TermPayment> query = entityManager.createNamedQuery("SELECT OMX_PLAN_ID, PLAN_ID,DECODE(plan_id,0,:user_input_total_amount,first_payment) first_paymenmt From (SELECT OMX_PLAN_ID, PLAN_ID,(SELECT DECODE(fraction,0,fixed_payment_amount, (( fraction/100) * :user_input_total_amount)) From TFN.VW_OMX_PAYMENT_PLAN_DETAILS i WHERE o.OMX_PLAN_ID=i.OMX_PLAN_ID AND i.OMX_PLAN_ID=:omxPlanId AND i.PAYMENT_ID=1) first_payment FROM TFN.VW_OMX_PAYMENT_PLANS o ) WHERE OMX_PLAN_ID=:omxPlanId ORDER by 1", TermPayment.class);
query.setParameter("omxPlanId", planId);
query.setParameter("user_input_total_amount", totalAmount);
return query.getSingleResult();
}
Return class
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class TermPayment {
#Id
#Column(name = "OMX_PLAN_ID")
Integer omxPlanId;
#Column(name = "PLAN_ID")
Integer planId;
#Column(name = "FIRST_PAYMENT")
Double firstPayment;
public Integer getOmxPlanId() {
return omxPlanId;
}
public void setOmxPlanId(Integer omxPlanId) {
this.omxPlanId = omxPlanId;
}
public Integer getPlanId() {
return planId;
}
public void setPlanId(Integer planId) {
this.planId = planId;
}
public Double getFirstPayment() {
return firstPayment;
}
public void setFirstPayment(Double firstPayment) {
this.firstPayment = firstPayment;
}
}
From the java.persistence.EntityManager javadoc:
/**
* Create an instance of <code>Query</code> for executing a named query
* (in the Java Persistence query language or in native SQL).
* #param name the name of a query defined in metadata
* #return the new query instance
* #throws IllegalArgumentException if a query has not been
* defined with the given name or if the query string is
* found to be invalid
*/
public Query createNamedQuery(String name);
so you should create your named query and only refer to it using createNamedQuery.
If you want to create a query from a string, you can use the createQuery(String query, Class type) method.
You can replace the method used in your DAO:
entityManager.createNamedQuery(...)
For this one:
entityManager.createQuery("select OMX_PLAN_ID, PLAN_ID ...", TermPayment.class)
Alternatively, you can create a NamedQuery adding a NamedQuery annotation in your Entity class or using xml. Afterthat, you could use the NamedQuery passing the NamedQuery name to the createNamedQuery() method.
#NamedQuery(name="MyQuery", query="select OMX_PLAN_ID, PLAN_ID ...")
entityManager.createNamedQuery(MyQuery, TermPayment.class);
You are trying to use NamedQuery api for creating dynamic queries which is wrong.
From doc
createNamedQuery method is used to create static queries, or queries that are defined in metadata by using the javax.persistence.NamedQuery annotation. The name element of #NamedQuery specifies the name of the query that will be used with the createNamedQuery method. The query element of #NamedQuery is the query:
#NamedQuery(
name="findAllCustomersWithName",
query="SELECT c FROM Customer c WHERE c.name LIKE :custName"
)
Here’s an example of createNamedQuery, which uses the #NamedQuery:
#PersistenceContext
public EntityManager em;
...
customers = em.createNamedQuery("findAllCustomersWithName")
.setParameter("custName", "Smith")
.getResultList();
You need to do something like,
entityManager.createQuery(
"SELECT c FROM Customer c WHERE c.name LIKE :custName")
.setParameter("custName", name)

Getting pl/sql array of struct from stored procedure using JdbcSimpleCall

im trying to execute oracle stored procedure using SimpleJDBCCall, all tables and stored procedures are in restaurant schema, table looks like:
CREATE TABLE STAFF
(
STAFF_ID NUMBER(5),
STAFF_FIRST_NAME VARCHAR2(10 BYTE) NOT NULL,
STAFF_LAST_NAME VARCHAR2(20 BYTE) NOT NULL,
STAFF_ROLE VARCHAR2(20 BYTE) NOT NULL,
STAFF_OTHER_DETAILS VARCHAR2(50 BYTE)
);
my type package:
CREATE OR REPLACE PACKAGE Staff_Types
AS
TYPE Staff_Collection IS TABLE OF Staff%ROWTYPE;
END Staff_Types;
my access package:
CREATE OR REPLACE PACKAGE Staff_TAPI
AS
FUNCTION getAllStaff RETURN Staff_Types.Staff_Collection;
END Staff_TAPI;
CREATE OR REPLACE PACKAGE BODY Staff_Tapi
AS
FUNCTION getAllStaff
RETURN Staff_Types.Staff_Collection
IS
all_staff Staff_Types.Staff_Collection;
BEGIN
SELECT *
BULK COLLECT INTO all_staff
FROM Staff;
RETURN all_staff;
END;
END Staff_Tapi;
Java Access:
#Component
#Qualifier("staffJdbcDAO")
public class StaffJDBCDAO implements StaffDAO {
JdbcTemplate jdbcTemplate;
SimpleJdbcCall getAllMembersSP;
#Autowired
#Qualifier("dataSource")
DataSource dataSource;
#Autowired
#Qualifier("jdbcTemplate")
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
initializeStoredProceduresCalls();
}
private void initializeStoredProceduresCalls() {
getAllMembersSP = new SimpleJdbcCall(jdbcTemplate);
getAllMembersSP.withCatalogName("Staff_Tapi");
getAllMembersSP.withFunctionName("getAllStaff");
getAllMembersSP.declareParameters(
new SqlOutParameter("return",
Types.OTHER,
"Staff_Types.Staff_Collection",
new SqlReturnStructArray<>( new StaffMapper() )
)
);
getAllMembersSP.compile();
}
#Override
public List<Staff> getAllMembers() {
Staff[] staff = getAllMembersSP.executeFunction(Staff[].class,new HashMap<String,Object>() );
return Arrays.asList(staff);
}
}
mapping class:
public class StaffMapper implements StructMapper<Staff> {
#Override
public STRUCT toStruct(Staff staff, Connection connection, String typeName) throws SQLException {
StructDescriptor descriptor = StructDescriptor.createDescriptor(typeName, connection);
Object[] attributes = new Object[5];
attributes[0] = new Integer( staff.getId() );
attributes[1] = new String("STAFF_FIRST_NAME");
attributes[2] = new String("STAFF_LAST_NAME");
attributes[3] = new String("STAFF_ROLE");
attributes[4] = new String("STAFF_OTHER_DETAILS");
Struct staffStruct = connection.createStruct(typeName,attributes);
return new STRUCT(descriptor,connection,attributes);
}
#Override
public Staff fromStruct(STRUCT struct) throws SQLException {
StructDescriptor descriptor = struct.getDescriptor();
ResultSetMetaData metaData = descriptor.getMetaData();
Object[] attributes = struct.getAttributes();
Map<String,Object> attributeMap = new HashMap<>();
int idx = 1;
for ( Object attribute : attributes )
attributeMap.put( metaData.getColumnName(idx++),attribute );
int id = ((Integer)attributeMap.get("STAFF_ID")).intValue();
String firstName = (String) attributeMap.get("STAFF_FIRST_NAME");
String lastName = (String) attributeMap.get("STAFF_LAST_NAME");
String staffRole = (String) attributeMap.get("STAFF_ROLE");
String otherDetails = (String) attributeMap.get("STAFF_OTHER_DETAILS");
return new Staff(id,firstName,lastName,staffRole,otherDetails);
}
}
and staff:
public class Staff {
private int id;
private String firstName;
private String lastName;
private String profession;
private String otherDetails;
public Staff(int id, String firstName, String lastName, String profession, String otherDetails) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.profession = profession;
this.otherDetails = otherDetails;
}
public int getId() {
return id;
}
public int setId(int id) {
this.id = id;
}
// and others getters and setters
}
when i execute getAllMembers from StaffDAO im constatly getting :
CallableStatementCallback; uncategorized SQLException for SQL
[{? = call STAFF_TAPI.GETALLSTAFF()}];
SQL state [99999]; error code [17004]; Invalid column type: 1111;
when i change return type parameter to Types.Array i get:
CallableStatementCallback; uncategorized SQLException for SQL
[{? = call STAFF_TAPI.GETALLSTAFF()}];
SQL state [99999]; error code [17074];
invalid name pattern: restaurant.Staff_Types.Staff_Collection;
i tried in both ways with pattern "Staff_Types.Staf_collection" getting same results, im trying to do this for nearly 2 days without any idea what should i do, if anyone has any suggestions i will be greateful.
You cannot load a PL/SQL record from a stored procedure through JDBC. In fact, you cannot even load such a type from Oracle SQL. See also this question for details:
Fetch Oracle table type from stored procedure using JDBC
You can only load SQL types through JDBC (as opposed to PL/SQL types). Given your example, you'll need to write:
-- You cannot really avoid this redundancy
CREATE TYPE STAFF AS OBJECT
(
STAFF_ID NUMBER(5),
STAFF_FIRST_NAME VARCHAR2(10 BYTE) NOT NULL,
STAFF_LAST_NAME VARCHAR2(20 BYTE) NOT NULL,
STAFF_ROLE VARCHAR2(20 BYTE) NOT NULL,
STAFF_OTHER_DETAILS VARCHAR2(50 BYTE)
);
CREATE TYPE STAFF_TABLE AS TABLE OF STAFF;
And then:
CREATE OR REPLACE PACKAGE Staff_TAPI
AS
FUNCTION getAllStaff RETURN STAFF_TABLE;
END Staff_TAPI;
In order to easily integrate your PL/SQL call, and since it is already built as a function: have you thought about something like this?
select * from TABLE(CAST(Staff_Tapi.getAllStaff() as Staff_Types.Staff_Collection))
This way, you can execute it easily as a regular JDBC query. Once done, just process the ResultSet it returns with some minor variant of your fromStruct method in order to return a List<Staff> list to whatever business logic you have on top of it. Hope you find this useful!
You may want to capitalize your custom type in java code like
getAllMembersSP.declareParameters(
new SqlOutParameter("return",
Types.OTHER,
"STAFF_TYPES.STAFF_COLLECTION",
new SqlReturnStructArray<>( new StaffMapper() )
)
);

Trying to create a List of user-defined objects, getting Object List

This is my user.java domain class is
public class User {
private Integer iduser;
private String user_name;
private String user_activation_key;
private String user_password;
private String user_email;
private Character registered;
public Integer getIduser() {
return iduser;
}
public void setIduser(Integer iduser) {
this.iduser = iduser;
}
public String getUser_name() {
return user_name;
}.....more code
And in a another Test class I have Coded as follow.
SQLQuery query = session.createSQLQuery("select * from user");
List<User> availableUsers = new ArrayList<User>();
availableUsers=(List<User>)query.list();
query is a SQLQuery (Hibernate implementation) to get user list from DB.
But I can't cast the (query.list) to List<User>.
I'm getting class cast exception. availableUser is a object list.
What steps should I follow.
Use HQL
SQLQuery query = session.createQuery("from User"); //note here User is POJO class name
List<User> availableUsers = new ArrayList<User>();
availableUsers=(List<User>)query.list();
18.1.1. Scalar queries
These will return a List of Object arrays (Object[]) with scalar values for each column in the user table. Hibernate will use ResultSetMetadata to deduce the actual order and types of the returned scalar values.
Use addScalar() method when using createSQLQuery see more
query.list() returns List<Object>.
Each object is actually an Object array (Object[]) which has the whole row.
For example, if you are getting all the Employee results, then the list is as follows:-
{ {emp_id,emp_name,other_emp_col} , {emp_id,emp_name,other_emp_col}, {emp_id,emp_name,other_emp_col} ....}
So for retrieving them you will have to iterate the list of Object and cast iterator.next() into an Object array and then construct your own User object.
For example:-
List<Object> userList = query.list();
Iterator<Object> itr = userList.iterator();
while(itr.hasNext()){
Object[] userRow = (Object[]) itr.next();
User userObj = new User();
userObj.setName(userRow[0]);
}
To save all these efforts as Aniket said, use HQL

Categories