Context
I'm creating a database environment where I'd like to split data in several different schemas to be used for different groups of users. Although, one of these databases should be shared to everyone due to it contains common entities.
Suppose databases:
DB1 - Common entities;
Wheels entity
DB2 - Group "A";
Cars entity
DB3 - Group "B";
Motorcycles entity
I have three different projects:
Project 1:
Wheels bean
Project 2:
Cars constructor
Project 3:
Motorcycles constructor
Problem
I'm trying to access wheels (Project 1) from projects/schemas (2,"A") and (3,"B")
First question: Is it possible?
Second: How can I do it?
hibernate.cfg.xml in project 2 is configured to
<property name="hibernate.connection.url">jdbc:mysql://99.999.999.99:3306/DB2</property>
This necessarily must restrict all the connections to DB2, or there's another way to add a new connection or work with all databases in 3306 port, or at least DB1?
Mapping the entities from project1 in project 2 seems not to be succeeded too, like:
<mapping class="com.company.project1.Wheels"
package="com.company.project1.Wheels" resource="com/company/project1/Wheels.hbm.xml"/>
Configuration
Eclipse Indigo
MySql 5.5
Hibernate 3.0 (mapping through xml instead annotations)
Win 7
Thanks for helping!
You can use #Table(catalog="") to specify database to which they belong to and then also can make relation across database.
in your case Wheel maps to DB1, Car to DB2 and MotorCycle to DB3 using catalog attribute.
i have used this solution with MySQL and MSSQL and works perfectly fine. only constraint this has all three DB has to be in same database server and user which is being used to access db should have appropriate permission to all DB.
As this solution just adds schema name against table in all queries.
I would divide my project in multiple self sustained projects. The Wheel project will be self sufficient project which takes care of Wheel entity.
Project 1: Wheel
This project will define Hibernate entities and DAO to access / modify wheel definitions.
Also I would configure a separate datasource in this project which points to DB1.
Entity classes:
#Entity
public class Wheel {
}
DAO classes:
#Repository
public class WheelDAO {
#Persistence
private EntityManager em;
}
Basically the idea is to separate application at DAO level. And manage transactions at Service level. Imaging WheelDAO (wired to DB1 datasource) and CarDAO (wired to DB2 datasource) and inject these in CarService.
DB1 DB2 DB2
| | |
WheelDAO CarDAO MotorcycleDAO
\_____________/ |
\_____|__________________________/
| |
| |
CarService MotorCycleService
I suggest to use Spring as IOC container to manage these dependency. Although you can achieve this without using Spring too.
What you need is just a db connection factory which allows you to use db that you want when you need it.
Take a look at the class below which you can adapte to resolve your issue
import java.net.URL;
import java.util.HashMap;
import javax.security.auth.login.Configuration;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static HashMap<String, SessionFactory> sessionFactoryMap = new HashMap<String, SessionFactory>();
public static final ThreadLocal sessionMapsThreadLocal = new ThreadLocal();
public static Session currentSession(String key) throws HibernateException {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
if(sessionMaps == null) {
sessionMaps = new HashMap();
sessionMapsThreadLocal.set(sessionMaps);
}
// Open a new Session, if this Thread has none yet
Session s = (Session) sessionMaps.get(key);
if(s == null) {
s = ((SessionFactory) sessionFactoryMap.get(key)).openSession();
sessionMaps.put(key, s);
}
return s;
}
public static Session currentSession() throws HibernateException {
return currentSession("");
}
public static void closeSessions() throws HibernateException {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
sessionMapsThreadLocal.set(null);
if(sessionMaps != null) {
for(Session session : sessionMaps.values()) {
if(session.isOpen())
session.close();
}
;
}
}
public static void closeSession() {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
sessionMapsThreadLocal.set(null);
if(sessionMaps != null) {
Session session = sessionMaps.get("");
if(session != null && session.isOpen())
session.close();
}
}
public static void buildSessionFactories(HashMap<String, String> configs) {
try {
// Create the SessionFactory
for(String key : configs.keySet()) {
URL url = HibernateUtil.class.getResource(configs.get(key));
SessionFactory sessionFactory = new Configuration().configure(url).buildSessionFactory();
sessionFactoryMap.put(key, sessionFactory);
}
} catch(Exception ex) {
ex.printStackTrace(System.out);
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
} // end of the try - catch block
}
public static void buildSessionFactory(String key, String path) {
try {
// Create the SessionFactory
URL url = HibernateUtil.class.getResource(path);
SessionFactory sessionFactory = new Configuration().configure(url).buildSessionFactory();
sessionFactoryMap.put(key, sessionFactory);
} catch(Throwable ex) {
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
} // end of the try - catch block
}
public static void closeSession(String key) {
HashMap<String, Session> sessionMaps = (HashMap<String, Session>) sessionMapsThreadLocal.get();
if(sessionMaps != null) {
Session session = sessionMaps.get(key);
if(session != null && session.isOpen())
session.close();
}
}
} // end of the class
http://www.java-forums.org/
Related
hi I Configured second level cache/query cache in my spring mvc - hibernate application.
Configuration Part:
#EH Cache
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
hibernate.cache.use_query_cache=true
HibernateProperties:
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.hbm2ddl.auto", environment.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));
properties.put("hibernate.hbm2ddl.auto", environment.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.cache.use_second_level_cache", environment.getProperty("hibernate.cache.use_second_level_cache"));
properties.put("hibernate.cache.region.factory_class",
environment.getProperty("hibernate.cache.region.factory_class"));
properties.put("hibernate.cache.use_query_cache","hibernate.cache.use_query_cache");
// properties.put("", environment.getProperty(""));
return properties;
}
Entity:
#Entity
#Cache(usage=CacheConcurrencyStrategy.READ_ONLY,region="myLpn")
public class MyLpn {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="myLpnSeq")
#SequenceGenerator(name="myLpnSeq",sequenceName="MYLPN_SEQ")
private Long lpnId;
private String tcLpnId;
//I have all setter/gettters properly
Service Impl:
#Transactional
public MyLpn getLpnById(Long lpnId) {
MyLpn lpn = null;
try {
lpn = lpnDaoImpl.getLpnById(lpnId);
} catch (DataAccessException dataAccessException) {
System.out.println();
throw new DataAccessResourceFailureException("Exception Occured");
}
return lpn;
}
DAOImple:
public MyLpn getLpnById(Long lpnId) {
Session currentSession = sessionFactory.getCurrentSession();
Query query = currentSession.createQuery("FROM MyLpn myLpn WHERE lpnId = :lpnId");
query.setLockMode("myLpn", LockMode.PESSIMISTIC_WRITE);
query.setParameter("lpnId", lpnId);// "lpnId"
query.setCacheable(true);
List<?> returnedList = query.list();
return (MyLpn) returnedList.get(0);
}
But my doubt is
When i request an Entity I see SQL query in the app logs. This is fine, lets suppose that For the first time Entity has been fetched from DB and Stored in Second->First Level Cache.
But When I request for the same entity second time again I see the SQL in the app logs. This is making me confuse.
Can you please let me know why Hibernate is executing a SQL even i configured both second level and query cache.
public class Register {
#Autowired
private DataSource dataSource;
#Autowired
private DCNListener listener;
private OracleConnection oracleConnection = null;
private DatabaseChangeRegistration dcr = null;
private Statement statement = null;
private ResultSet rs = null;
#PostConstruct
public void init() {
this.register();
}
private void register() {
Properties props = new Properties();
props.put(OracleConnection.DCN_NOTIFY_ROWIDS, "true");
props.setProperty(OracleConnection.DCN_IGNORE_DELETEOP, "true");
props.setProperty(OracleConnection.DCN_IGNORE_UPDATEOP, "true");
try {
oracleConnection = (OracleConnection) dataSource.getConnection();
dcr = oracleConnection.registerDatabaseChangeNotification(props);
statement = oracleConnection.createStatement();
((OracleStatement) statement).setDatabaseChangeRegistration(dcr);
rs = statement.executeQuery(listenerQuery);
while (rs.next()) {
}
dcr.addListener(listener);
String[] tableNames = dcr.getTables();
Arrays.stream(tableNames)
.forEach(i -> log.debug("Table {}" + " registered.", i));
} catch (SQLException e) {
e.printStackTrace();
close();
}
}
}
My Listener:
public class DCNListener implements DatabaseChangeListener {
#Override
public void onDatabaseChangeNotification(DatabaseChangeEvent databaseChangeEvent) {
TableChangeDescription[] tableChanges = databaseChangeEvent.getTableChangeDescription();
for (TableChangeDescription tableChange : tableChanges) {
RowChangeDescription[] rcds = tableChange.getRowChangeDescription();
for (RowChangeDescription rcd : rcds) {
RowOperation op = rcd.getRowOperation();
String rowId = rcd.getRowid().stringValue();
switch (op) {
case INSERT:
//process
break;
case UPDATE:
//do nothing
break;
case DELETE:
//do nothing
break;
default:
//do nothing
}
}
}
}
}
In my Spring boot application, I have an Oracle DCN Register class that listens for INSERTS in an event table of my database. I am listening for insertion new records.
In this Event table, I have different types of events that my application supports, lets say EventA and EventB.
The application gui allows you to upload in bulk these type of events which translate into INSERT into the oracle database table I am listening to.
For one of the event types, my application is not capturing the INSERT ONLY when it is 20 or more events uploaded in bulk, but for the other event type, I do not experience this problem.
So lets say user inserts eventA any number < 20, my application captures the inserts. But if the number of eventA inserts exceeds 20, it does not capture.
This is not the case for eventB which works smoothly. I'd like to understand if I'm missing anything in term of registration and anything I can look out for maybe in the database or what the issue could be here?
You should also look for the ALL_ROWS event from:
EnumSet<TableChangeDescription.TableOperation> tableOps = tableChange.getTableOperations();
if(tableOps.contains(TableChangeDescription.TableOperation.ALL_ROWS)){
// Invalidate the cache
}
Quote fromt the JavaDoc:
The ALL_ROWS event is sent when the table is completely invalidated and row level information isn't available. If the DCN_NOTIFY_ROWIDS option hasn't been turned on during registration, then all events will have this OPERATION_ALL_ROWS flag on. It can also happen in situations where too many rows have changed and it would be too expensive for the server to send the list of them.
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/jajdb/oracle/jdbc/dcn/TableChangeDescription.TableOperation.html#ALL_ROWS
I use Kundera-Cassandra 3.2 and want to use the transaction management from Kundera.
My handling looks like this:
EntityManager manager = repo.getEntityManagerFactory().createEntityManager(CassandraRepository.getProperties());
try{
manager.getTransaction().begin();
this.repo.update(account1, manager); //calls the merge method of the Entitymanager
this.repo.save(account2, manager); //calls the persist method of the Entitymanager
manager.getTransaction().commit();
} catch(Exception e){
if(manager.getTransaction().isActive()){
manager.getTransaction().rollback();
}
} finally {
manager.clear();
manager.close();
}
When an error in the this.repo.save(account2, manager); occurs, the manager rollbacks the transaction, but does not do a update statement, he makes a delete statement for the merge method. The reason for this is, when calling the merge methode, kundera creates an insert statement and not an update. But how to say Kundera to make an update to rollback the transaction also with an update.
Logs:
12:42:41.185 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000004'.
12:42:41.211 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000005'.
EDIT (my repository):
public class CassandraRepository<T> {
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public static Map<String, String> getProperties() {
final Map<String, String> properties = new HashMap<String, String>();
properties.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);
return properties;
}
public void update(T entity, EntityManager manager) throws Exception{
try {
manager.merge(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public void save(T entity, EntityManager manager) throws Exception{
try {
manager.persist(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
According to JPA, to update an entity you have to first bring it into managed state (by fetching it)
Example:-
PersonCassandra p = entityManager.find(PersonCassandra.class, "2");
entityManager.getTransaction().begin();
p.setMonth(Month.JAN);
entityManager.merge(p);
entityManager.persist(p3);
entityManager.getTransaction().commit();
Issue is not with INSERT and UPDATE statements since both are similar for Cassandra, under the hood.
I am trying to fetch data from table I am using following code to fetch data from db.
public List<UserInfoSetting> fetchAll(Long aid) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
List<UserInfoSetting> obj = null;
try {
String hql = "select s from UserInfoSetting s where s.atom.id=:aid ";
Query query = session.createQuery(hql);
query.setParameter("aid", aid);
obj = query.list();
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
tx.rollback();
}
} finally {
session.close();
}
return obj;
}
HibernateUtil.java
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
It is showing following exception
root cause
java.lang.NoClassDefFoundError: Could not initialize class business.HibernateUtil
setting.user.UserCommunicationDao.fetchAll(UserCommunicationDao.java:146)
setting.user.UserCommunication.fetchAll(UserCommunication.java:64)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:483)
How to resolve the above problem
This means either you haven't got all the right Hibernate libraries in your class path, or you're not including the classes you've written. If you're coding using Eclipse, find the project settings and add the Hibernate libraries as dependencies for the project.
You will need to add hibernate-core, but quite a few others too.
Maven would help...
And it would help you a lot if you changed the name of your HibernateUtil class: there is a standard Hibernate class with the same name. Although in principle you can have two classes with the same name but in different packages, it'll be likely to cause confusion. (For instance, it's not entirely clear which one it can't find.)
I have 2 methods:
public static Ticket issueTicket(User user,Service service,String[] seats) {
Session ticSess= DB.factory.openSession();
ticSess.beginTransaction();
Date d= new Date();
Ticket ticket=new Ticket(d, service, user);
ticSess.save(ticket);
ticSess.getTransaction().commit();
int seatCount=seats.length;
for (int i=0;i<seatCount;i++){
int seatID=Integer.parseInt(seats[i]);
Seat seat=getSeatByID(seatID);
seat.setTicket(ticket);
ticSess.update(seat);
}
return ticket;
}
and,
public static Seat getSeatByID(int seatID) {
Session proSess = DB.factory.openSession();
proSess.beginTransaction();
Seat c = (Seat) (proSess.load(Seat.class, seatID));
proSess.getTransaction().commit();
return c;
}
when I call issueTicket method I get:
illegally attempted to associate a proxy with two open Sessions
and If I close the session in getSeatByID method there will be another error telling that the session is closed. Here is the Stack Trace:
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at ir.ac.shirazu.cse.Terminal.Seat_$$_javassist_9.setTicket(Seat_$$_javassist_9.java)
at ir.ac.shirazu.cse.Database.DB.issueTicket(DB.java:231)
Try closing proSess in getSeatByID() before returning. Currently the Seat indeed remains attached to session opened in getSeatByID().
I got same problem . But after using singleton pattern for session i'm done. I'm using Hibernate 4.2.x.
This is my session class is used to get sessions for DB transactions etc.
public class SessionClass {
static Session session = PoolManager.getSession();
public static Session getSession() {
if (session != null || session.isOpen()) {
return session;
} else {
session = PoolManager.getSession();
return session;
}
}
}
Hibernate Helper Class I'm using.
public class PoolManager {
private static final SessionFactory sessionFactory;
private static final ServiceRegistry serviceRegistry;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
// Log the exception.
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() {
return sessionFactory.openSession();
}
}
I ran into this issue when trying to associate a entity from a Envers Session.
Fixed that by "refreshing" said entity (retrieving it on my non-Envers session via PK fetch) before bubbling it up to my algorithm.
use session.opensession().get(.....).. instead of session.opensession().load(.....)
what if you do a proSess.evict(c) before committing proSess?