How to use #Formula in Hibernate/JPA - java

I am trying to see how #Formula annotation works using a simple piece of code below.
I am able to print out values of description and bidAmount columns but the fields annotated with #Formula i.e. shortDescription and averageBidAmount return null.
Can anyone please help point out what is wrong with the code here?
I am using Hibernate 5.0, postgresql-9.3-1102-jdbc41 and TestNG on a Mac OSX.
import java.math.BigDecimal;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import javax.transaction.UserTransaction;
import org.testng.annotations.Test;
import com.my.hibernate.env.TransactionManagerTest;
public class DerivedPropertyDemo extends TransactionManagerTest {
#Test
public void storeLoadMessage() throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("HelloWorldPU");
try {
{
UserTransaction tx = TM.getUserTransaction();
tx.begin();
EntityManager em = emf.createEntityManager();
DerivedProperty derivedProperty1 = new DerivedProperty();
derivedProperty1.description = "Description is freaking good!!!";
derivedProperty1.bidAmount = BigDecimal.valueOf(100D);
DerivedProperty derivedProperty2 = new DerivedProperty();
derivedProperty2.description = "Description is freaking bad!!!";
derivedProperty2.bidAmount = BigDecimal.valueOf(200D);
DerivedProperty derivedProperty3 = new DerivedProperty();
derivedProperty3.description = "Description is freaking neutral!!!";
derivedProperty3.bidAmount = BigDecimal.valueOf(300D);
em.persist(derivedProperty1);
em.persist(derivedProperty2);
em.persist(derivedProperty3);
tx.commit();
for (DerivedProperty dp : getDerivedProperty(em)) {
System.out.println("============================");
System.out.println(dp.description);
System.out.println(dp.bidAmount);
System.out.println(dp.getShortDescription());
System.out.println(dp.getAverageBidAmount());
System.out.println("#############################");
}
em.close();
}
} finally {
TM.rollback();
emf.close();
}
}
public List<DerivedProperty> getDerivedProperty(EntityManager em) {
List<DerivedProperty> resultList = em.createQuery("from " + DerivedProperty.class.getSimpleName()).getResultList();
return resultList;
}
}
My Entity class is:
#Entity
class DerivedProperty {
#Id
#GeneratedValue
protected Long id;
protected String description;
protected BigDecimal bidAmount;
#org.hibernate.annotations.Formula("substr(description, 1, 12)")
protected String shortDescription;
#org.hibernate.annotations.Formula("(select avg(b.bidAmount) from DerivedProperty b where b.bidAmount = 200)")
protected BigDecimal averageBidAmount;
public String getShortDescription() {
return shortDescription;
}
public BigDecimal getAverageBidAmount() {
return averageBidAmount;
}
}
EDIT
I am following the book Java Persistence with Hibernate 2nd Ed.
Thanks

Your DerivedProperty instances are returned from the persistence context (only their ids are used from the result set returned from the query). That's why formulas haven't been evaluated.
Persistence context is not cleared if you don't close the entity manager. Try adding em.clear() after you commit the first transaction to force clearing the persistence context.

Related

Hibernate could not determine type for: java.util.Collection in many to many relation

I am learning manytomany relationship in hibernate from the tutorial. My example project has two tables called product_table and order_table. I was using many to many relationship between these two tables.
While executing my code I found the following error in the console:
Exception in thread "main" org.hibernate.MappingException: Could not determine type for: java.util.Collection, at table: product_details, for columns: [org.hibernate.mapping.Column(orders)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:456)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:423)
at org.hibernate.mapping.Property.isValid(Property.java:226)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:597)
at org.hibernate.mapping.RootClass.validate(RootClass.java:265)
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:329)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:459)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:710)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:726)
at com.hibernatetest.main.MainApp.hibernateSession(MainApp.java:98)
at com.hibernatetest.main.MainApp.main(MainApp.java:93)
Here's my Hibernate configuration file:
<!-- ~ Hibernate, Relational Persistence for Idiomatic Java ~ ~ License:
GNU Lesser General Public License (LGPL), version 2.1 or later. ~ See the
lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- In case of using MySQL greater than 5 use MYSQL5 Dialect in stead of mysqldialect -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume test is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_test?createDatabaseIfNotExist=true</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping class="com.hibernatetest.dto.ProductDetails" />
<mapping class="com.hibernatetest.dto.OrderDetails" />
</session-factory>
</hibernate-configuration>
Here's my OrderDetails classes:
package com.hibernatetest.dto;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
#Entity
#Table(name = "product_details")
public class ProductDetails {
private int productId;
private String productName;
#ManyToMany(mappedBy="product_details")
private Collection<OrderDetails> orders=new ArrayList();
public ProductDetails() {
}
public ProductDetails(String productName) {
this.productName = productName;
}
#Id
#Column(name = "product_id")
#GeneratedValue(strategy = GenerationType.AUTO)
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
#Column(name = "product_name")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Collection<OrderDetails> getOrders() {
return orders;
}
public void setOrders(Collection<OrderDetails> orders) {
this.orders = orders;
}
}
Here is my OrderDetails class:
package com.hibernatetest.dto;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name = "order_details")
public class OrderDetails {
private int orderId;
private String orderName;
#ManyToMany
private Collection<ProductDetails> products = new ArrayList();
public OrderDetails() {
super();
}
public OrderDetails(String orderName) {
super();
this.orderName = orderName;
}
#Id
#Column(name = "order_id")
#GeneratedValue(strategy = GenerationType.AUTO)
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
#Column(name = "order_name")
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public Collection<ProductDetails> getProduct() {
return products;
}
public void setProduct(Collection<ProductDetails> products) {
this.products = products;
}
}
Here is MainApp class:
package com.hibernatetest.main;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.hibernatetest.dto.Address;
import com.hibernatetest.dto.Office;
import com.hibernatetest.dto.OrderDetails;
import com.hibernatetest.dto.ProductDetails;
import com.hibernatetest.dto.UserDetails;
public class MainApp {
public static void main(String[] args) {
/*
* UserDetails user = new UserDetails(); UserDetails user2=new UserDetails();
* Address address1=new Address("test street1", "test city1", "test state",
* "0000"); Address address2=new Address("test street2", "test city2",
* "test state", "0001"); //user.setUserId(2); user.setUsername("Test User 1");
* user2.setUsername("Test User 2"); user.setJoinedDate(new Date());
* user2.setJoinedDate(new Date()); user.setHomeAddress(address1);
* user.setOfficeAddress(address2); user2.setHomeAddress(address2);
* user2.setOfficeAddress(address1); user.setDescription("test data 1");
* user2.setDescription("test data 2"); user.setJoinedTime(new Date());
* user2.setJoinedTime(new Date());
*
* String officePhone1="00000000"; String officePhone2="00000001"; String
* officePhone3="00000002"; Collection<String> phoneNumbers=new
* ArrayList<String>(); phoneNumbers.add(officePhone1);
* phoneNumbers.add(officePhone2); phoneNumbers.add(officePhone3); Office
* office=new Office(1,"Test Office 1", address1,phoneNumbers);
*
* SessionFactory sessionFactory = new
* Configuration().configure().buildSessionFactory();
*
* Session session = sessionFactory.openSession();
*
* session.beginTransaction();
*
* session.save(user); session.save(user2); session.save(office);
*
* session.getTransaction().commit();
*
* session.close();
*
* user=null;
*
* session= sessionFactory.openSession(); session.beginTransaction();
* user=session.get(UserDetails.class,2);
*
* System.out.println(user.getUserId()+" "+user.getDescription());
*
* session.close(); office=null; session=sessionFactory.openSession();
* session.beginTransaction(); office=session.get(Office.class, 1);
* System.out.println(office.getOfficeName()); session.close();
* System.out.println(office.getPhoneList().size());
*/
ProductDetails product1 = new ProductDetails("Sample product 1");
ProductDetails product2 = new ProductDetails("Sample product 2");
ProductDetails product3 = new ProductDetails("Sample product 3");
ProductDetails product4 = new ProductDetails("Sample product 4");
OrderDetails order1 = new OrderDetails("Order No 1");
OrderDetails order2 = new OrderDetails("Order No 2");
product1.getOrders().add(order1);
product1.getOrders().add(order2);
product2.getOrders().add(order2);
order1.getProduct().add(product1);
order1.getProduct().add(product2);
order1.getProduct().add(product3);
order2.getProduct().add(product1);
order2.getProduct().add(product3);
order2.getProduct().add(product4);
List<Object> insetableObjects = new ArrayList<Object>();
insetableObjects.add(product1);
insetableObjects.add(product2);
insetableObjects.add(product3);
insetableObjects.add(product4);
insetableObjects.add(order1);
insetableObjects.add(order2);
hibernateSession(insetableObjects);
}
public static void hibernateSession(List<Object> collection) {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
for (Object obj : collection) {
session.save(obj);
System.out.println("Object Added");
}
session.getTransaction().commit();
session.close();
}
}
Please guide me to the next steps and thanks in advance.
See this tutorial and Jack Flamp's comments. In order to establish a ManyToMany relationship, you need a #JoinTable referencing the table you want to use in its specific class, and a reference in the other. For instance, in your case it would be something like:
ProductDetails:
#Entity
#Table(name = "product_details")
public class ProductDetails {
private int productId;
private String productName;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "order_product",
joinColumns = { #JoinColumn(name = "productId") },
inverseJoinColumns = { #JoinColumn(name = "orderId") }
)
private Collection<OrderDetails> orders = new ArrayList();
[...]
OrderDetails:
#Entity
#Table(name = "order_details")
public class OrderDetails {
private int orderId;
private String orderName;
#ManyToMany(mappedBy = "orders")
private Collection<ProductDetails> products = new ArrayList();
Here, your "owning side" (= the one holding the information) is the product. Your product can have multiple orders, and an order belongs to multiple products. Of course, feel free to revert them as you wish if it does not suit your needs. The link I gave you explains well what each annotation does.
EDIT: Make sure that (in the example) the order_product table actually exists. Otherwise, it won't work.

Fetching data from table using hibernate

I am able to persist objects in relational database using hibernate.
please look at following code.
package one;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SecondaryTable;
import javax.persistence.Table;
#Entity
public class Customer {
#Id
private int customerId;
private String customerName;
private String customerAddress;
private int creditScore;
private int rewardPoints;
public Customer()
{
}
public Customer(int customerId,String customerName,String customerAddress,int creditScore,int rewardsPoints)
{
this.customerId=customerId;
this.customerAddress=customerAddress;
this.creditScore=creditScore;
this.customerName=customerName;
this.rewardPoints=rewardsPoints;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(String customerAddress) {
this.customerAddress = customerAddress;
}
public int getCreditScore() {
return creditScore;
}
public void setCreditScore(int creditScore) {
this.creditScore = creditScore;
}
public int getRewardPoints() {
return rewardPoints;
}
public void setRewardPoints(int rewardPoints) {
this.rewardPoints = rewardPoints;
}
}
Then to save object of this class i used following class. following class creates the object of class Customer and saves that object in database then again retrieves it and prints the CustomerName property of every saved object.
package one;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class TestCustomer {
public static void main(String args[])
{
Customer cust = new Customer(13,"Sara","Banglore",9000,60);
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
session.beginTransaction();
session.save(cust);
session.getTransaction().commit();
session.close();
session = factory.openSession();
session.beginTransaction();
List list = session.createQuery("FROM Customer").list();
Iterator iterator = list.iterator();
while(iterator.hasNext())
{
Customer custA = (Customer)iterator.next();
System.out.println("First Name\t"+custA.getCustomerName());
}
session.getTransaction().commit();
session.close();
}
}
I executed above code quite a number of times. code is running fine. it is able to fetch all objects which are saved.
but then i used oracle toad and fired a sql statement as
Insert into Customer(CUSTOMERID,CREDITSCORE,CUSTOMERNAME,REWARDPOINTS,CUSTOMERADDRESS)
VALUES(87,4000,'Saurabh',20,'Kalwa');
record gets stored in the table but when i execute above code, i am not able to fetch this record.
one conclusion i can draw is hibernate only returns persisted objects, but still is there any other way i can get all records ?
Are you sure you have submitted the record after inserting with toad for oracle?(you can open another client and execute a select to make sure it can be fetched from sql client).
If you want to debug, you can enable the sql logging function of hibernate, and then execute the sql which hibernate generates for your query in a sql client to make sure all the records can be fetched correctly.
And some suggestions for using JPA:
Make sure the #Entity has a name value which mapping to your physical table to avoid table mapping confusion.
Use #Column(name="column") for all your fields to mapping to the physical table column to avoid confusion.

Difficulty implementing PUT annotation in my Rest service

I am using Java,Maven,Hibernate 3/JPA ,Eclipse to implement a PUT method for populating a Mysql db.
Here is my POJO
import static javax.persistence.GenerationType.IDENTITY;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
#Entity
#Table(name = "Person", catalog = "mydb", uniqueConstraints = {
#UniqueConstraint(columnNames = "Person"),})
public class Person implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String Name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name", unique = true, nullable = false, length = 30)
public String getName() {
return flowName;
}
public void setName(String Name) {
this.Name = Name;
}
}
Here is my annotations class.
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import com.google.gson.Gson;
import com.tracker.domain.Flow;
import com.tracker.persistence.HibernateUtil;
public class PersonService {
private Logger LOG = Logger.getLogger(TrackerService.class);
String JsonString = "{\"name\":\"John Doe\"}";
Gson gson = new Gson();
Person person = gson.fromJson(JsonString,Person.class);
#PUT
#Path("")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public void processandSaveJson(Person person) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
String Name = Person.getName();
person.setName(Name);
session.beginTransaction();
session.save(person);
session.getTransaction().commit();
}
}
Here is my Hibernate.Util.
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new AnnotationConfiguration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
Here is my SessionFactory Context Listener class
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.apache.log4j.Logger;
import org.hibernate.Session;
#WebListener
public class SessionFactoryListener implements ServletContextListener {
private Logger LOG = Logger.getLogger(SessionFactoryListener.class);
#Override
public void contextInitialized(ServletContextEvent arg0) {
if (LOG.isInfoEnabled()) {
LOG.info("\n\tInside contextInitialized()---\n");
}
Session session = HibernateUtil.getSessionFactory().openSession();
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
if (LOG.isInfoEnabled()) {
LOG.info("\n\tInside contextDestroyed()\n");
}
HibernateUtil.shutdown();
}
}
When I try to run this using Tomcat Server, i get the following error.
type Status report
message Method Not Allowed
description The specified HTTP method is not allowed for the requested resource.
I am very new to this. Kindly let me know what I am doing wrong. I trying to insert a
record into a mysql db using the above values. Kindly help me out.
Thanks,
Jack
as mentioned in the comments, you should supply your calling code along with the rest. but since you already mentioned that you're using a browser to make the request, it should be mentioned that most/no browsers support 'put' without using javadcript. what you are doing looks like a simple 'get'.
so the solution is to either use javascript in your form submission, or discard REST and have Urls that reflect the method (eg. /person/new/ and /person/{personId}

Hibernate Query Cache issue

I've found a strange bug in my app. I've simplified it, and that is how it can be reproduced:
(I used DbUnit to create the tables and HSQLDB as a database, but that doesn't actually matter)
package test;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.orm.hibernate3.HibernateTemplate;
public class DatabaseBugReproduction {
#Entity(name = "A")
#Table(name = "a")
public static class A {
private int id;
private Set <B> bs;
#Id
public int getId() {
return id;
}
#ManyToMany
#JoinTable(
name = "ab",
joinColumns = #JoinColumn(name = "a_id"),
inverseJoinColumns = #JoinColumn(name = "b_id")
)
public Set <B> getBs() {
return bs;
}
void setId(int id) {
this.id = id;
}
void setBs(Set <B> engines) {
this.bs = engines;
}
}
#Entity(name = "B")
#Table(name = "b")
public static class B {
private int id;
#Id
public int getId() {
return id;
}
void setId(int id) {
this.id = id;
}
}
private static SessionFactory getSessionFactory() throws SQLException, IOException, DatabaseUnitException {
String driverClass = "org.hsqldb.jdbc.JDBCDriver";
String jdbcUrl = "jdbc:hsqldb:mem:seoservertooltest";
String dbUsername = "test";
String dbPassword = "test";
String dbDialect = "org.hibernate.dialect.HSQLDialect";
Configuration config = new Configuration()//
.setProperty("hibernate.connection.driver_class", driverClass)//
.setProperty("hibernate.connection.url", jdbcUrl)//
.setProperty("hibernate.connection.username", dbUsername)//
.setProperty("hibernate.connection.password", dbPassword)//
.setProperty("hibernate.dialect", dbDialect)//
.setProperty("hibernate.hbm2ddl.auto", "create-drop")//
.setProperty("hibernate.current_session_context_class", "thread")//
.setProperty("hibernate.cache.use_query_cache", "true")//
.setProperty("hibernate.cache.use_second_level_cache", "true")//
.setProperty("hibernate.cache.region.factory_class", "net.sf.ehcache.hibernate.EhCacheRegionFactory")//
.setProperty("hibernate.cache.region_prefix", "")//
// .setProperty("hibernate.show_sql", "true")//
// .setProperty("hibernate.format_sql", "true")//
.addAnnotatedClass(A.class) //
.addAnnotatedClass(B.class) //
;
SessionFactory result = config.buildSessionFactory();
try (Connection con = DriverManager.getConnection(jdbcUrl, dbUsername, dbPassword)) {
con.createStatement().executeUpdate("SET DATABASE REFERENTIAL INTEGRITY FALSE;");
String xml = "<?xml version='1.0' encoding='UTF-8'?>"//
+ "<dataset>"//
+ "<a id='1'/>"//
+ "<b id='1'/>"//
+ "<ab a_id='1' b_id='1' />"//
+ "</dataset>";
final IDatabaseConnection dbCon = new DatabaseConnection(con);
try {
final FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
final IDataSet dataSet = builder.build(new StringReader(xml));
DatabaseOperation.CLEAN_INSERT.execute(dbCon, dataSet);
} finally {
dbCon.close();
}
}
return result;
}
public static void main(String[] args) throws Exception {
HibernateTemplate hibTemplate = new HibernateTemplate(getSessionFactory());
hibTemplate.setCacheQueries(true);
System.out.println(hibTemplate.find("select a.bs from A a"));
System.out.println(hibTemplate.find("select a.bs from A a"));
}
}
Output is:
[test.DatabaseBugReproduction$B#2942ce]
[null]
It looks like the cache is somehow misconfigured. Where is a mistake and how can I fix it?
Used:
JDK 1.7.0_01 both x32 and x64
Hibernate 3.6.7
Ehcache 2.5.0
Spring 3.1.0
Database: works at least with HSQLDB, H2 and MySQL
After some debugging, I've found that there seems to be a problem with the Query Cache and collection queries. The method that dissembles collections to store in the cache always returns null.
In fact, after googling it up, it turns out that this problems is due to a bug in Hibernate. See the issue description for more information.
While this problem isn't fixed (it seems like it won't) you could re-write your query so you don't need a collection query:
public static void main(String[] args) throws Exception {
HibernateTemplate hibTemplate = new HibernateTemplate(getSessionFactory());
hibTemplate.setCacheQueries(true);
//System.out.println(hibTemplate.find("select a.bs from A a"));
//System.out.println(hibTemplate.find("select a.bs from A a"));
System.out.println(hibTemplate.find("select bs from A a inner join a.bs as bs"));
System.out.println(hibTemplate.find("select bs from A a inner join a.bs as bs"));
}
I've tested it and it works fine.

Hibernate exception handling

I've got a little 'complex' question.
I'm using Hibernate/JPA to make transactions with a DB.
I'm not the DBA, and a client consumes my application, a RESTful web service. My problem is that the DB is altered (not very often, but it still changes). Also, the client does not always respect input for my application (length, type, etc.). When this happens Hibernate throws an exception. The exception is difficult to translate and read from the log, because it has nested exceptions and consists of a lot of text: like I said, very difficult to understand.
I want to know if it's possible to handle exceptions on entity level, throwing maybe a customized exception.
I thank your patience and help in advance.
EDIT:
Fianlly I managed to do what I wanted, not sure if it's done the right way.
App.java
package com.mc;
import org.hibernate.Session;
import com.mc.stock.Stock;
import com.mc.util.HibernateUtil;
import javax.persistence.EntityManager;
public class App {
public static void main(String[] args) {
Set<ConstraintViolation<Stock>> violations;
validator = Validation.buildDefaultValidatorFactory().getValidator();
Scanner scan = new Scanner(System.in);
EntityManager em = null;
System.out.println("Hibernate one to many (Annotation)");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
String nextLine = scan.nextLine();
stock.setStockCode(nextLine.toString());
nextLine = scan.nextLine();
stock.setStockName(nextLine.toString());
violations = validator.validate(stock);
if (violations.size() > 0) {
StringBuilder excepcion = new StringBuilder();
for (ConstraintViolation<Stock> violation : violations) {
excepcion.append(violation.getMessageTemplate());
excepcion.append("\n");
}
System.out.println(excepcion.toString());
}
session.save(stock);
session.getTransaction().commit();
}
}
FieldMatch.java
package com.mc.constraints;
import com.mc.constraints.impl.FieldMatchValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = FieldMatchValidator.class)
#Documented
public #interface FieldMatch {
String message() default "{constraints.fieldmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String first();
String second();
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#interface List {
FieldMatch[] value();
}
}
FieldMatchValidator.java
package com.mc.constraints.impl;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.mc.constraints.FieldMatch;
import org.apache.commons.beanutils.BeanUtils;
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(final FieldMatch constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
}
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
try {
final Object firstObj = BeanUtils.getProperty(value, firstFieldName);
final Object secondObj = BeanUtils.getProperty(value, secondFieldName);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
} catch (final Exception ignore) {
// ignore
}
return true;
}
}
Stock.java
package com.mc.stock;
import com.mc.constraints.FieldMatch;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.validator.constraints.Length;
#Entity
#Table(name = "STOCK")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Stock.findAll", query = "SELECT s FROM Stock s"),
#NamedQuery(name = "Stock.findByStockId", query = "SELECT s FROM Stock s WHERE s.stockId = :stockId"),
#NamedQuery(name = "Stock.findByStockCode", query = "SELECT s FROM Stock s WHERE s.stockCode = :stockCode"),
#NamedQuery(name = "Stock.findByStockName", query = "SELECT s FROM Stock s WHERE s.stockName = :stockName")})
#FieldMatch.List({
#FieldMatch(first = "stockCode", second = "stockName", message = "Code and Stock must have same value")
})
public class Stock implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_stock_id")
#SequenceGenerator(name = "seq_stock_id", sequenceName = "seq_stock_id", initialValue = 1, allocationSize = 1)
#Basic(optional = false)
#Column(name = "STOCK_ID", unique = true, nullable = false)
private Integer stockId;
#Column(name = "STOCK_CODE")
private String stockCode;
#Length(min = 1, max = 20, message = "{wrong stock name length}")
#Column(name = "STOCK_NAME")
private String stockName;
public Stock() {
}
public Stock(Integer stockId) {
this.stockId = stockId;
}
public Integer getStockId() {
return stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
public String getStockCode() {
return stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
public String getStockName() {
return stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
#Override
public int hashCode() {
int hash = 0;
hash += (stockId != null ? stockId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Stock)) {
return false;
}
Stock other = (Stock) object;
if ((this.stockId == null && other.stockId != null) || (this.stockId != null && !this.stockId.equals(other.stockId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.mc.stock.Stock[ stockId=" + stockId + " ]";
}
}
HibernateUtil.java
package com.mc.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
Oracle DB Structure
CREATE TABLE stock
(
STOCK_ID NUMBER(5) NOT NULL ,
STOCK_CODE VARCHAR2(10) NULL ,
STOCK_NAME VARCHAR2(20) NULL
);
ALTER TABLE stock
add CONSTRAINT PK_STOCK_ID PRIMARY KEY (STOCK_ID);
create sequence seq_stock_id
start with 1
increment by 1
nomaxvalue;
I'm inclined to do as much validation before you get the the DB level. Have a look at Hibernate Validator, http://www.hibernate.org/subprojects/validator.html which is the reference implementation of JSR-303.
Using standard annotations you can enforce constraints and get good error messages before you attempt to put the entities into your database.
I believe this will allow you to validate at the entity level as requested.
I am not sure what you mean about "entity level", but sure. Put a try/catch around the code that is invoking Hibernate. Catch Throwable and rethrow whatever you want. The trick is putting some reason that makes sense in the exception you are throwing.
Of course, one major point is that you should validate all input.
You can implement your own SQLExceptionConverter and handle it the way you want.
Use the property 'hibernate.jdbc.sql_exception_converter' to set your custom converter.
I am unable to find more documentation this, you need to dig into implementations by Hibernate to find more.
By the way, why can't you have a global filter, which catches every exception and decide which exception to re throw as it is or throw a new exception? You will be doing more or less same even if you implement your own SQLExceptionConverter.
according to my experience, you should catch the SQLException, and then u can get easily the SQL error code for specific database.
Eg: your database is mysql and u got error code 1062 . So you can know that error is Duplicated entry error. You can check the mysql error code
http://www.briandunning.com/error-codes/?source=MySQL

Categories

Resources