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.
Related
I have two entities called FeeTerms.java and FeeTermDates.java
I want to get all matched records from these two entities using pure HQL
Look at entities:
FeeTerms.java
package com.rasvek.cg.entity;
// Generated May 14, 2018 11:39:07 PM by Hibernate Tools 5.1.7.Final
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* FeeTerms generated by hbm2java
*/
#Entity
#Table(name = "fee_terms", catalog = "campus_guru_01")
public class FeeTerms implements java.io.Serializable {
private Integer termId;
private String termName;
private String termCount;
private Set<FeeTermDates> feeTermDateses = new HashSet<FeeTermDates>(0);
private Set<AssocFeeTerms> assocFeeTermses = new HashSet<AssocFeeTerms>(0);
public FeeTerms() {
}
public FeeTerms(String termName, String termCount, Set<FeeTermDates> feeTermDateses,
Set<AssocFeeTerms> assocFeeTermses) {
this.termName = termName;
this.termCount = termCount;
this.feeTermDateses = feeTermDateses;
this.assocFeeTermses = assocFeeTermses;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "term_id", unique = true, nullable = false)
public Integer getTermId() {
return this.termId;
}
public void setTermId(Integer termId) {
this.termId = termId;
}
#Column(name = "term_name")
public String getTermName() {
return this.termName;
}
public void setTermName(String termName) {
this.termName = termName;
}
#Column(name = "term_count", length = 45)
public String getTermCount() {
return this.termCount;
}
public void setTermCount(String termCount) {
this.termCount = termCount;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "feeTerms")
public Set<FeeTermDates> getFeeTermDateses() {
return this.feeTermDateses;
}
public void setFeeTermDateses(Set<FeeTermDates> feeTermDateses) {
this.feeTermDateses = feeTermDateses;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "feeTerms")
public Set<AssocFeeTerms> getAssocFeeTermses() {
return this.assocFeeTermses;
}
public void setAssocFeeTermses(Set<AssocFeeTerms> assocFeeTermses) {
this.assocFeeTermses = assocFeeTermses;
}
}
FeeTermDates.java
package com.rasvek.cg.entity;
// Generated May 14, 2018 11:39:07 PM by Hibernate Tools 5.1.7.Final
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* FeeTermDates generated by hbm2java
*/
#Entity
#Table(name = "fee_term_dates", catalog = "campus_guru_01")
public class FeeTermDates implements java.io.Serializable {
private int tdmId;
private FeeTerms feeTerms;
private String date;
public FeeTermDates() {
}
public FeeTermDates(int tdmId, FeeTerms feeTerms) {
this.tdmId = tdmId;
this.feeTerms = feeTerms;
}
public FeeTermDates(int tdmId, FeeTerms feeTerms, String date) {
this.tdmId = tdmId;
this.feeTerms = feeTerms;
this.date = date;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "tdm_id", unique = true, nullable = false)
public int getTdmId() {
return this.tdmId;
}
public void setTdmId(int tdmId) {
this.tdmId = tdmId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "term_id", nullable = false)
public FeeTerms getFeeTerms() {
return this.feeTerms;
}
public void setFeeTerms(FeeTerms feeTerms) {
this.feeTerms = feeTerms;
}
#Column(name = "date")
public String getDate() {
return this.date;
}
public void setDate(String date) {
this.date = date;
}
}
i have tried with following code but i am not getting it
String hql="select FT.termId , FT.termName , FT.termCount,FT.feeTermDateses from FeeTerms FT ,FeeTermDates FD where FT.termId=FD.feeTerms" ;
query = currentSession.createQuery(hql);
termDatesList= query.getResultList();
how to achieve it as pure HQL. i am very new to Hibernate and HQl.
i have got something like below in another post,
public List<Category> getCategoryList(int id) {
List<Category> groupList;
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("select c from Category c join fetch c.events where c.parentCategory.categoryId = 1");
//query.setParameter("id", id);
groupList = query.list();
return groupList;
}
Is it possible to achieve my query as above done?
You can receive a list of Object[] with the values that you want. Like:
String hql="select FT.termId , FT.termName , FT.termCount, FT.feeTermDateses from FeeTerms FT, FeeTermDates FD where FT.termId = FD.feeTerms.id";
Query query = currentSession.createQuery(hql);
List<Object[]> results = query.getResultList();
for (Object[] obj : results) {
Integer termId = obj[0];
String termName = obj[1];
String termCount = obj[2];
Set<FeeTermDates> feeTermDates = obj[4];
}
But, I could suggest a better version:
String hql = "SELECT ft FROM FeeTerms ft JOIN ft.feeTermDateses feeTermDateses";
Query query = currentSession.createQuery(hql);
List<FeeTerms> results = query.getResultList();
This already brings to you all FeeTerms that have FeeTermDates.
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.
These are my pojo class
Orderdetail.java
package online.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "orderdetail")
public class OrderDetail {
#Id
#Column(name="order_detail_id")
private int order_detail_id;
#Column(name="bill")
private float bill;
#ManyToOne
#JoinColumn(name = "p_id" )
private Product p_id;
#ManyToOne
#JoinColumn(name = "o_id" )
private Order o_id;
public int getOrder_detail_id() {
return order_detail_id;
}
public void setOrder_detail_id(int order_detail_id) {
this.order_detail_id = order_detail_id;
}
public float getBill() {
return bill;
}
public void setBill(float bill) {
this.bill = bill;
}
public Product getP_id() {
return p_id;
}
public void setP_id(Product p_id) {
this.p_id = p_id;
}
public Order getO_id() {
return o_id;
}
public void setO_id(Order o_id) {
this.o_id = o_id;
}
}
My Order.java
package online.model;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
#Entity
#Table(name = "ordertable")
public class Order {
#Id
#Column(name = "order_id")
private int order_id;
#OneToMany(mappedBy = "o_id",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<OrderDetail> orderdetail;
#ManyToOne
#JoinColumn(name = "u_id")
private UserDetail u_id;
public UserDetail getU_id() {
return u_id;
}
public void setU_id(UserDetail u_id) {
this.u_id = u_id;
}
#Column(name = "date")
#Temporal(TemporalType.TIMESTAMP)
private Date date;
#Column(name = "totalbill")
private Float totalbill;
public Float getTotalbill() {
return totalbill;
}
public void setTotalbill(Float totalbill) {
this.totalbill = totalbill;
}
public List<OrderDetail> getOrderdetail() {
return orderdetail;
}
public void setOrderdetail(List<OrderDetail> orderdetail) {
this.orderdetail = orderdetail;
}
public int getOrder_id() {
return order_id;
}
public void setOrder_id(int order_id) {
this.order_id = order_id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
When ever I am trying to save order class I want my orderdetail class also get saved but when I am trying to save the List in order,Is is not getting saved and there is not error provided by hibernate that can help...
Thanks for the help
when i am trying to to persist the order class
Hibernate: select orderdetai_.order_detail_id, orderdetai_.bill as bill7_, orderdetai_.o_id as o3_7_, orderdetai_.p_id as p4_7_ from orderdetail orderdetai_ where orderdetai_.order_detail_id=?
This what I am getting output.
This is my code which save the class
#Override
public boolean payment(String username, Integer ordernumber, Date date,
Float totalbill, List<Integer> list) {
Session session = sessionFactory.openSession();
Transaction tranction = session.beginTransaction();
try {
Query query = session
.createQuery("from UserDetail where user_username = :username");
query.setParameter("username", username);
List<UserDetail> userdetaillist = query.list();
UserDetail userdetail = userdetaillist.get(0);
query = session
.createQuery("from ProductDetail where product_detail_id in(:list)");
query.setParameterList("list", list);
List<ProductDetail> productdetail = query.list();
Order order = new Order();
order.setOrder_id(ordernumber);
order.setDate(date);
order.setU_id(userdetail);
order.setTotalbill(totalbill);
List<OrderDetail> orderdetail = new ArrayList<OrderDetail>();
OrderDetail ordetail = new OrderDetail();
for (ProductDetail pro : productdetail) {
ordetail.setO_id(order);
ordetail.setP_id(pro.getProduct_id());
ordetail.setBill(pro.getProduct_id().getProduct_sell_price());
orderdetail.add(ordetail);
}
System.out.print("totalbill" + totalbill);
System.out.println(orderdetail);
order.setOrderdetail(orderdetail);
session.save(order);
tranction.commit();
return true;
} catch (Exception e) {
tranction.rollback();
e.getStackTrace();
}
return false;
}
I think ordetail has to be created inside the for.. You are modifying the same object for each productdetail. Should be like this:
List<OrderDetail> orderdetail = new ArrayList<OrderDetail>();
OrderDetail ordetail = null;
for (ProductDetail pro : productdetail) {
ordetail = new OrderDetail();
ordetail.setO_id(order);
ordetail.setP_id(pro.getProduct_id());
ordetail.setBill(pro.getProduct_id().getProduct_sell_price());
orderdetail.add(ordetail);
}
Hey I have recheck my pojo class and I found out the mistake I have done. I have made change and it work properly now.
I was not setting the the id for Orderdetail table. It was auto increment in database.
So it was giving me error ""
So I have made change in orderdetail iD
"#GeneratedValue(strategy=GenerationType.AUTO)" So now It is working fine cause now hibernate know that the id will get value from database.
Thanks for the help and for your time
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}
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