Executing Oracle Stored Procedure with Hibernate - java

I have been trying to execute an Oracle Stored Procedure using Hibernate. This is not for production - but for a Java source parsing project, that I'm pursuing. To put it in simple terms, I'm not able to return a value from an Oracle Stored Proc.
I have searched and read all relevant links from SO, Hibernate community/documentation(Native SQL chapter) links and tried out the suggestions, but somehow couldn't get them to work. Below are my sources - I'm including only the relevant parts.
My Entity Class. I have reserved the first parameter for a PL/SQL OUT parameter.
Login.java
#NamedNativeQuery(
name = "getLoginDet",
query = "call GET_LOGIN_DET(?,:userId)",
resultClass = Login.class)
#Entity
#Table(name = "T_LOGIN_DET")
public class Login {
Oracle Stored Proc : GET_LOGIN_DET.sql. The first parameter is OUT REFCURSOR as per Hibernate Spec
create or replace PROCEDURE GET_LOGIN_DET(listLogin OUT SYS_REFCURSOR,userId IN VARCHAR2)
AS
BEGIN
OPEN listLogin FOR
SELECT *
FROM T_LOGIN_DET
WHERE USER_ID = userId;
END GET_LOGIN_DET;
My DAO Class : I'm binding only the named parameter, ignoring the first ? in the named Query.
Session session = sessionFactory.openSession();
List results = session.getNamedQuery("getLoginDet").setParameter("userId", u.getUserId()).list();
My Hibernate Config
<bean id="mysessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="annotatedClasses">
<list>
<value>com.cogn.gto.sea.employee.entity.Employee</value>
<value>com.cogn.gto.sea.employee.entity.Department</value>
<value>com.cogn.gto.sea.login.entity.User</value>
<value>com.cogn.gto.sea.login.entity.Login</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
However, I always end up with Hibernate QueryException / Java SQLException
Expected positional parameter count: 1, actual parameters: [] [{call GET_LOGIN_DET(?,:userId)}]
I have tried variations of {? = call GET_LOGIN_DET(:userId)} , call GET_LOGIN_DET(?,:userId) to no avail. My requirement is to call the procedure that I have listed and get the result back in the DAO class. Can someone lead me to what exactly I'm missing here ?

I believe you have incorrectly declared call to stored procedure (curly brackets missing), try this:
#NamedNativeQuery(
name = "getLoginDet",
query = "{call GET_LOGIN_DET(?,:userId)}",
resultClass = Login.class
hints = {#QueryHint(name = "org.hibernate.callable", value = "true")})
#Entity
#Table(name = "T_LOGIN_DET")
public class Login {

Related

Gaps In the sequence values generated by JPA #GeneratedValue with PostgreSQL

For my desktop application I use JavaFX, Spring, JPA + Hibernate and PostgreSQL. Currently I have faced several issues.
Issue one: Violation of PRIMARY KEY constraint SQL Error
When I create Entity classes according to following manner (GenerationType as AUTO) it works fine. But when I create a new data base and add some Test data with sql script (as showing following picture) and try to insert some data with my application I have got 'Violation of PRIMARY KEY constraint' SQL Error. That mean it seems Hibernate try to generated PK values which are already available (allocated with my test data ex 1, 2, 3 etc). But after 5 attempts (exceed the test data maximum pk value) it was fine and start the data inserting with PK key value with 6.
Entity Class - GenerationType as AUTO
#Entity
#Table(name = "devicetype")
public class Devicetype implements Serializable {
#Id
#Basic(optional = false)
#Column(name = "id")
private Integer id;
}
Table with initial test data
EntityManager factory
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan" value="com.core.domain" />
<property name="jpaProperties">
<props>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgresPlusDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.use_sql_comments">false</prop>
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
</props>
</property>
</bean>
Issue Two: Gaps In the sequence value
For make resolve about error I have changed Entity class according to following manner (with GenerationType as SEQUENCE) and done the same steps (insert initial test data with sql script and try to insert data via application). Then data was inserted without any exception. Now my table contains records both inserted via script and application (as showing following picture). But newly add data (I have highlight on light blue color) via application had very higher PK value (started at 184 not from 6). That mean with "GenerationType as SEQUENCE" it seems hibernate not populate ID value in sequence manner (maintain some gaps). When I add further some data via application it seems it will stat to inserting data with another higher ID value (not start form 214). That mean is seems ID is not incrementing on sequence manner.
Entity Class - GenerationType as SEQUENCE
#Entity
#Table(name = "device_type")
public class DeviceType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name="my_seq", sequenceName="MY_SEQ", allocationSize=1, initialValue=1)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
}
Table data with gaps (for sequence ID)
This is how sequences (and so the PostgreSQL SERIAL type) behave.
If you are manually inserting values then you will need to update the sequence accordingly. Or, more usually, don't manually insert the values and let the sequence do it.
Gaps are inevitable unless you want to lock the table on each insert and kill off any hope of concurrency. I'd recommend not caring.
Remember - the numbers don't mean anything, they are just a convenient identifier.
Spend a few minutes reading up on how this all works: CREATE SEQUENCE, ALTER SEQUENCE

Injecting queries into DAOs via a spring config file

I'm looking at my project's DAO class which has native sql query in java class. there are different queries based on different condition. I'm planning to take out these query from java class. It can be something like this -
<bean name="hibernateRuleDao" class="com.xyz.dao.RuleDao">
<property name="ruleForSystemQuery">
<value>
select distinct rule from NormalRule as rule
inner join fetch rule.dimensions dimensions
where rule.system = :system and rule.status = :status
</value>
</property>
<property name="ruleAuditQuery">
<value>
select ra from RuleAudit ra where ra.rule.name =
:ruleName
</value>
</property>
</bean>
Is this the right approach ? Any other better approach to do this ?
Note: Using hibernate/jpa named query is not feasible in my use case.
If the purpose is to facilitate SQL modification without code recompilation, then yes this will do. You just need to inject RuleDao into your DAO classes.
However in my opinion SQL query modification should go through standard software lifecycle (design, implement, test, operate). So I'm happy with my SQL queries being hardcoded into DAO classes.

How do I use Hibernate's second level cache with JPA?

I am implementing an Entity Attribute Value based persistence mechanism. All DB access is done via Hibernate.
I have a table that contains paths for nodes, it is extremely simple, just an id, and a path (string) The paths would be small in number, around a few thousand.
The main table has millions of rows, and rather than repeating the paths, I've normalized the paths to their own table. The following is the behaviour I want, when inserting into main table
1) Check if the path exists in paths table (query via entity manager, using path value as parameter)
2) if it does not exist, insert, and get id (persist via entity manager)
3) put id as foreign key value to main table row, and insert this into main table.
This is going to happen thousands of times for a set of domain objects, which correspond to lots of rows in main table and some other tables. So the steps above are repeated using a single transaction like this:
EntityTransaction t = entityManager.getTransaction();
t.begin();
//perform steps given above, check, and then persist etc..
t.commit();
When I perform step 2, it introduces a huge performance drop to the total operation. It is begging for caching, because after a while that table will be at most 10-20k entries with very rare new inserts. I've tried to do this with Hibernate, and lost almost 2 days.
I'm using Hibernate 4.1, with JPA annotations and ECache. I've tried to enable query caching, even using the same query object throughout the inserts, as shown below:
Query call = entityManager.createQuery("select pt from NodePath pt " +
"where pt.path = :pathStr)");
call.setHint("org.hibernate.cacheable", true);
call.setParameter("pathStr", pPath);
List<NodePath> paths = call.getResultList();
if(paths.size() > 1)
throw new Exception("path table should have unique paths");
else if (paths.size() == 1){
NodePath path = paths.get(0);
return path.getId();
}
else {//paths null or has zero size
NodePath newPath = new NodePath();
newPath.setPath(pPath);
entityManager.persist(newPath);
return newPath.getId();
}
The NodePath entity is annotated as follows:
#Entity
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Table(name = "node_path", schema = "public")
public class NodePath implements java.io.Serializable {
The query cache is being used, as far as I can see from the statistics, but no use for second level cache is reported:
queries executed to database=1
query cache puts=1
query cache hits=689
query cache misses=1
....
second level cache puts=0
second level cache hits=0
second level cache misses=0
entities loaded=1
....
A simple, hand written hashtable as a cache, works as expected, cutting down total time drastically. I guess I'm failing to trigger Hibernate's caching due to nature of my operations.
How do I use hibernate's second level cache with this setup? For the record, this is my persistence xml:
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>...</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.connection.password" value="zyx" />
<property name="hibernate.connection.url" value="jdbc:postgresql://192.168.0.194:5432/testdbforml" />
<property name="hibernate.connection.username" value="postgres"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.search.autoregister_listeners" value="false"/>
<property name="hibernate.jdbc.batch_size" value="200"/>
<property name="hibernate.connection.autocommit" value="false"/>
<property name="hibernate.generate_statistics" value="true"/>
<property name="hibernate.cache.use_structured_entries" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
</properties>
Ok, I found it.
My problem was that, cached query was keeping only Ids of query results in the cache, and it was (probably) going back to db to get the actual values, rather than getting them from the second level cache.
The problem is of course, the query did not put those values to second level cache, since they were not selected by primary id. So the solution is to use a method that will put values to second level cache, and with hibernate 4.1, I've manage to do this with natural id. Here is the function that either inserts or returns the value from cache, just in case it helps anybody else:
private UUID persistPath(String pPath) throws Exception{
org.hibernate.Session session = (Session) entityManager.getDelegate();
NodePath np = (NodePath) session.byNaturalId(NodePath.class).using("path", pPath).load();
if(np != null)
return np.getId();
else {//no such path entry, so let's create one
NodePath newPath = new NodePath();
newPath.setPath(pPath);
entityManager.persist(newPath);
return newPath.getId();
}
}

Issue with hibernate/Spring transaction management

I am facing a problem with Spring Transaction management. I am using hibernate as ORM framework. And below is my spring for transaction management.
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="abstractDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true" lazy-init="true">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref bean="transactionAttributeSource"/>
</property>
<property name="postInterceptors">
<list>
<ref bean="finderIntroductionAdvisor"/>
</list>
</property>
</bean>
<bean id="abstractService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true" lazy-init="true">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref bean="transactionAttributeSource"/>
</property>
</bean>
I am basically following genericDao approach as mentioned here so My DaoObject are used to fetch the Domain objects and service classes have DAO objects to maipulate them.
issue i am facing : i am querying a large dataset and loading the result in a list inside the service class. i have marked service class as #transactional(readonly ="true").
to handle some reuirement i have changed all the getters of boxed primitive to
#Column(name = "students")
public Long getStudents() {
if(students== null){
return 0l;
}
return this.students;
}
whenever i load all the dataset via a named query. multiple update queries are fired subsequently to update the dataset.
I debugged that and came to know that this is occuring because of transaction.commit. as hibernate is treating my entities as dirty.
is there a way i can avoid this . i know Flushmode.never could be of help but in my application seesion object is not exposed so i do not have access to it. is there any other way or some mapping change which can help me ?
In addition to Xavi López's answer, another option is to separate persistent property handled by Hibernate from the transient property that conform to your requirement. For example, as follows:
#Column(name = "students")
public Long getStudentsInternal() {
return students;
}
#Transient
public Long getStudents() {
if (students == null) {
return 0l;
}
return students;
}
You can also configure Hibernate to use fields instead of properties by moving annotations to them, it will solve your problem as well (note that the placement of annotations should be consitent for all fields of the entity, or you can use #Access to configure an exclusion):
#Column(name = "students")
private Long students;
public Long getStudents() {
if (students == null) {
return 0l;
}
return students;
}
The issue is with
if(students== null){
return 0l;
}
When Hibernate fetches your entities, they all have null value on the studentsfield. At commit time, when checking if they are dirty, getStudents() returns 0, which is different from the value stored in the database. So, Hibernate sees them as dirty, and proceeds to the update.
If it is suitable to your requirement, changing the type of the studentfield to the primitive type long instead of Long would probably help. Note that this would lead to updating all null's in that column to 0 in the long term.
Maybe you should handle that requirement somewhere else, and free the getter from that concern.

Good way to generate SQL strings in java?

I'm not looking for a persistence layer like Hibernate, I just want to generate SQL-strings and they should be compatible with PreparedStatement. I've tried libraries such as Squiggle, but it only supports SELECT, I would also like to generate insert and updates. An ideal usage would be something like:
generateInsertOn("myTable").addValue("value1").addValue("value2").generate();
that would generate this string:
"INSERT INTO myTable (value1, value2) VALUES(?, ?)"
I know that there exists questions that are a lot like mine, such as this, but they don't quite ask the same thing as I do.
For arbitrary SQL, use jOOQ. jOOQ currently supports SELECT, INSERT, UPDATE, DELETE, TRUNCATE, and MERGE. You can create SQL like this:
// Since you're not executing the SQL, set connection to null
Connection connection = null;
Factory create = new MySQLFactory(connection);
String sql1 = create.select(A, B, C)
.from(MY_TABLE)
.where(A.equal(5))
.and(B.greaterThan(8))
.getSQL();
String sql2 = create.insertInto(MY_TABLE)
.values(A, 1)
.values(B, 2)
.getSQL();
String sql3 = create.update(MY_TABLE)
.set(A, 1)
.set(B, 2)
.where(C.greaterThan(5))
.getSQL();
The supported syntax is quite rich. You will also find support for clauses such as ON DUPLICATE KEY UPDATE, FOR UPDATE, LOCK IN SHARE MODE, etc.
For more details, see
http://www.jooq.org
(Disclaimer, I work for the company behind jOOQ)
You should definitively take a look at SQLBuilder. It allows simple, yet complete, SQL generation using a very fluent API.
Going out on a limb here, have you considered iBatis? It's a real down to earth query mapping framework (I hesitate to call it an ORM framework somehow). You have to create XML files like this one:
<mapper namespace="org.mybatis.jpetstore.persistence.ProductMapper">
<cache />
<select id="getProduct" parameterType="string" resultType="Product">
SELECT
PRODUCTID,
NAME,
DESCN as description,
CATEGORY as categoryId
FROM PRODUCT
WHERE PRODUCTID = #{productId}
</select>
</mapper>
which wires up a mapper like this one:
public interface ProductMapper {
Product getProduct(String productId);
}
Which allows you to access data from services like this:
#Autowired
private ProductMapper productMapper;
public Product getProduct(String productId) {
return productMapper.getProduct(productId);
}
Which you can wire up with Spring:
<!-- enable autowire -->
<context:annotation-config />
<!-- enable transaction demarcation with annotations -->
<tx:annotation-driven />
<!-- define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="org.mybatis.jpetstore.domain" />
</bean>
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.jpetstore.persistence" />
</bean>
See also the full petstore example.
I'm not an uniquivocal fan of iBatis but it might fit your needs in this specific case.

Categories

Resources