Hibernate can not write data to mysql correctly - java

I am trying to write some information about the user who opens the web application which i made. The web app is simple. When open it it shows users ip, user-agent, and the time of the last log into the app. I`ve made this app with these technologies: Spring, Maven, Tomcat, Hibernate and MySQL. It seems that everything is working correctly but when i open the app in browser in do note make a record to Data Base and i must start manually Main.java to make a record but the record is no correct because it does not record the real information but it record NULL everywhere. I have two questions - How to do all this automatically (when i open the web app to have a record in DB) and Why my records are every time NULL.
public class Main {
public static void main(String[] args) {
UserInfo user = new UserInfo();
user.setRollNo(7);
user.setIp(UserController.ip);
user.setUserAgent(UserController.userAgent);
user.setDate((Date) UserController.date);
SessionFactory sessionFactory = new AnnotationConfiguration()
.configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();
}
}
UserController.java
#Controller
public class UserController {
public static String ip;
public static String userAgent;
public static Date date;
#Autowired
private HttpServletRequest request;
#RequestMapping(value = "/connection")
public ModelAndView getUserInfo() {
ModelAndView modelandView = new ModelAndView("UserInfo");
ip = request.getRemoteAddr();
modelandView.addObject("msg1", "Your IP is " + ip);
userAgent = request.getHeader("User-Agent");
modelandView.addObject("msg2", "Your User agent is " + userAgent);
date = new java.util.Date();
/*
* Calendar calendar = Calendar.getInstance(); java.sql.Date startDate =
* new java.sql.Date(calendar.getTime() .getTime());
*/
modelandView.addObject("msg3", "Your last log was at " + date);
return modelandView;
}
}
UserInfo.java
#Entity
#Table(name = "USER_INFORMATION")
public class UserInfo {
#Id
private int rollNo;
private String ip;
private String userAgent;
private Date date;
public int getRollNo() {
return rollNo;
}
public void setRollNo(int rollNo) {
this.rollNo = rollNo;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/browserinfo</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">validate</property>
<mapping class="com.nikola.hellocontroller.UserInfo" />
</session-factory>
</hibernate-configuration>

According to this:
#novy154 I am trying to make e record to DB with the users IP, user-agent info and the time when the user was lastloged in the app. After that i want to display all records in the app.
First of all, you need to have some DataAccessLayer. Something like
public interface UserInfoRepository {
void save(UserInfo userInfo);
Collection<UserInfo> findAll();
}
and it's implementation.
Then maybe some service over the Repository, but it doesn't matter in this case, so let's skip it.
You need to inject your repository into controller and:
1) invoke userInfoRepository.save(newlyCreatedUserInfo)
2) pass all UserInfos to your view (by invoking userInfoRepository.findAll() and putting the result in modelAndView) and display it somehow.
Let me know if you need help implementing this.
But this is just in purpose of learning how to make simple crud-like application, cause this doesn't make any sense. You can't even identify your user, you just want to track each visitor's ip, user info and so on.
// edit:
Repository implementation would be something like:
#Repository
#Transactional
public class UserInfoRepositoryImpl implements UserInfoRepository {
#Autowired
private SessionFactory sessionFactory;
#Override
public void save(UserInfo userInfo) {
getCurrentSession.save(userInfo);
}
#Override
public Collection<UserInfo> findAll() {
final Criteria root = getCurrentSession().createCriteria(UserInfo.class);
return Collections.checkedList(root.list(), UserInfo.class);
}
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
Then in your controller method you need to invoke save(newlyCreatedUserInfo) to store your info in the database and
modelAndView.addObject("userInfos", repository.findAll());
to retrieve all data and pass it to a view.
Of course remember about
...
#Autowired
private UserInfoRepository userInfoRepository;
...
in controller.
Note that since spring-data-jpa provides you more powerful repositories out of the box, this is reinventing a wheel.

Related

Hibernate, nested transactions not supported

I have a trouble when running my web application that tries to get all the information from the table snowboard and put it in a list which I will then print out in the xHtml. But I get this exception down below.
org.hibernate.TransactionException: nested transactions not supported
The thing is that I have no clue why this exception happens so would appreciate some explanation. Also if you find any trouble in the code that would be fantastic.
This is the exception that I get.
HibernateUtil
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Log the exception.
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
SnowHelper, HelperClass
public class SnowHelper {
Session session = null;
public SnowHelper() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
public List getSnowboards() {
List<Snowboard> snowboardList = null;
try {
Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Snowboard");
snowboardList = (List<Snowboard>) q.list();
} catch (Exception e) {
e.printStackTrace();
}
return snowboardList;
}
}
HibernateCfg
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://cpsrv01.misshosting.com:3306/etvffqgz_snowshop</property>
<property name="hibernate.connection.username">etvffqgz_user</property>
<property name="hibernate.connection.password">759486456</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</property>
<mapping resource="Hibernate/Account.hbm.xml"/>
<mapping resource="Hibernate/Snowboard.hbm.xml"/>
</session-factory>
</hibernate-configuration>
SnowboardBean, ManagedBean class
#Named(value = "snowboardBean")
#Dependent
public class SnowboardBean {
private List<Snowboard> snowList;
private SnowHelper snow;
/**
* Creates a new instance of SnowboardBean
*/
public SnowboardBean() {
snowList = new ArrayList<>();
snow = new SnowHelper();
snowList = snow.getSnowboards();
}
/**
* #return the snowList
*/
public List<Snowboard> getSnowList() {
return snowList;
}
}
In this bit here.. commit your transaction
Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Snowboard");
snowboardList = (List<Snowboard>) q.list();
tx.commit();
Otherwise you simply open a new transaction without closing it every time you invoke this method.. eventually one of them is opened while some other is not commited yet.
If you were using 'container managed transactions' (provided by Spring of EJB's) you would not have to worry about explicitly committing your transactions. Here you are using 'extended transaction management' and you have to take care of that yourself.

Dynamic Multi-tenant WebApp (Spring Hibernate)

I've come up with a working dynamic multi-tenant application using:
Java 8
Java Servlet 3.1
Spring 3.0.7-RELEASE (can't change the version)
Hibernate 3.6.0.Final (can't change the version)
Commons dbcp2
This is the 1st time I've had to instantiate Spring objects myself so I'm wondering if I've done everything correctly or if the app will blow up in my face at an unspecified future date during production.
Basically, the single DataBase schema is known, but the database details will be specified at runtime by the user. They are free to specify any hostname/port/DB name/username/password.
Here's the workflow:
The user logs in to the web app then either chooses a database from a known list, or specifies a custom database (hostname/port/etc.).
If the Hibernate SessionFactory is built successfully (or is found in the cache), then it's persisted for the user's session using SourceContext#setSourceId(SourceId) then the user can work with this database.
If anybody choses/specifies the same database, the same cached AnnotationSessionFactoryBean is returned
The user can switch databases at any point.
When the user switches away from a custom DB (or logs off), the cached AnnotationSessionFactoryBeans are removed/destroyed
So will the following work as intended? Help and pointers are most welcome.
web.xml
<web-app version="3.1" ...>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener> <!-- Needed for SourceContext -->
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<web-app>
applicationContext.xml
<beans ...>
<tx:annotation-driven />
<util:properties id="db" location="classpath:db.properties" /> <!-- driver/url prefix -->
<context:component-scan base-package="com.example.basepackage" />
</beans>
UserDao.java
#Service
public class UserDao implements UserDaoImpl {
#Autowired
private TemplateFactory templateFactory;
#Override
public void addTask() {
final HibernateTemplate template = templateFactory.getHibernateTemplate();
final User user = (User) DataAccessUtils.uniqueResult(
template.find("select distinct u from User u left join fetch u.tasks where u.id = ?", 1)
);
final Task task = new Task("Do something");
user.getTasks().add(task);
TransactionTemplate txTemplate = templateFactory.getTxTemplate(template);
txTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
template.save(task);
template.update(user);
}
});
}
}
TemplateFactory.java
#Service
public class TemplateFactory {
#Autowired
private SourceSessionFactory factory;
#Resource(name = "SourceContext")
private SourceContext srcCtx; // session scope, proxied bean
#Override
public HibernateTemplate getHibernateTemplate() {
LocalSessionFactoryBean sessionFactory = factory.getSessionFactory(srcCtx.getSourceId());
return new HibernateTemplate(sessionFactory.getObject());
}
#Override
public TransactionTemplate getTxTemplate(HibernateTemplate template) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(template.getSessionFactory());
return new TransactionTemplate(txManager);
}
}
SourceContext.java
#Component("SourceContext")
#Scope(value="session", proxyMode = ScopedProxyMode.INTERFACES)
public class SourceContext {
private static final long serialVersionUID = -124875L;
private SourceId id;
#Override
public SourceId getSourceId() {
return id;
}
#Override
public void setSourceId(SourceId id) {
this.id = id;
}
}
SourceId.java
public interface SourceId {
String getHostname();
int getPort();
String getSID();
String getUsername();
String getPassword();
// concrete class has proper hashCode/equals/toString methods
// which use all of the SourceIds properties above
}
SourceSessionFactory.java
#Service
public class SourceSessionFactory {
private static Map<SourceId, AnnotationSessionFactoryBean> cache = new HashMap<SourceId, AnnotationSessionFactoryBean>();
#Resource(name = "db")
private Properties db;
#Override
public LocalSessionFactoryBean getSessionFactory(SourceId id) {
synchronized (cache) {
AnnotationSessionFactoryBean sessionFactory = cache.get(id);
if (sessionFactory == null) {
return createSessionFactory(id);
}
else {
return sessionFactory;
}
}
}
private AnnotationSessionFactoryBean createSessionFactory(SourceId id) {
AnnotationSessionFactoryBean sessionFactory = new AnnotationSessionFactoryBean();
sessionFactory.setDataSource(new CutomDataSource(id, db));
sessionFactory.setPackagesToScan(new String[] { "com.example.basepackage" });
try {
sessionFactory.afterPropertiesSet();
}
catch (Exception e) {
throw new SourceException("Unable to build SessionFactory for:" + id, e);
}
cache.put(id, sessionFactory);
return sessionFactory;
}
public void destroy(SourceId id) {
synchronized (cache) {
AnnotationSessionFactoryBean sessionFactory = cache.remove(id);
if (sessionFactory != null) {
if (LOG.isInfoEnabled()) {
LOG.info("Releasing SessionFactory for: " + id);
}
try {
sessionFactory.destroy();
}
catch (HibernateException e) {
LOG.error("Unable to destroy SessionFactory for: " + id);
e.printStackTrace(System.err);
}
}
}
}
}
CustomDataSource.java
public class CutomDataSource extends BasicDataSource { // commons-dbcp2
public CutomDataSource(SourceId id, Properties db) {
setDriverClassName(db.getProperty("driverClassName"));
setUrl(db.getProperty("url") + id.getHostname() + ":" + id.getPort() + ":" + id.getSID());
setUsername(id.getUsername());
setPassword(id.getPassword());
}
}
In the end I extended Spring's AbstractRoutingDataSource to be able to dynamically create datasources on the fly. I'll update this answer with the full code as soon as everything is working correctly. I have a couple of last things to sort out, but the crux of it is as follows:
#Service
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
// this is pretty much the same as the above SourceSessionFactory
// but with a map of CustomDataSources instead of
// AnnotationSessionFactoryBeans
#Autowired
private DynamicDataSourceFactory dataSourceFactory;
// This is the sticky part. I currently have a workaround instead.
// Hibernate needs an actual connection upon spring startup & there's
// also no session in place during spring initialization. TBC.
// #Resource(name = "UserContext") // scope session, proxy bean
private UserContext userCtx; // something that returns the DB config
#Override
protected SourceId determineCurrentLookupKey() {
return userCtx.getSourceId();
}
#Override
protected CustomDataSource determineTargetDataSource() {
SourceId id = determineCurrentLookupKey();
return dataSourceFactory.getDataSource(id);
}
#Override
public void afterPropertiesSet() {
// we don't need to resolve any data sources
}
// Inherited methods copied here to show what's going on
// #Override
// public Connection getConnection() throws SQLException {
// return determineTargetDataSource().getConnection();
// }
//
// #Override
// public Connection getConnection(String username, String password)
// throws SQLException {
// return determineTargetDataSource().getConnection(username, password);
// }
}
So I just wire up the DynamicRoutingDataSource as the DataSource for Spring's SessionFactoryBean along with a TransactionManager an all the rest as usual. As I said, more code to follow.

Hibernate get data .list() depricated and table not mapped. what to do?

I saw nearly all questions and answers for this on stackoverflow, but all answers are the same. That you should use something like this Query query = session.createQuery("from theme").list();
but .list() is depricated and I canĀ“t use it anymore.
So the first Question is, what should I use instead of .list()and why I get this exeption java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: theme is not mapped [from theme]
Query Method
public ObservableList<String> getThemes(){
ObservableList<String> obsList = FXCollections.observableArrayList();
SessionFactory factory = new Configuration()
.configure("hibernate.cfg.xml")
.buildSessionFactory();
Session session = factory.getCurrentSession();
try {
session.beginTransaction();
Query query = session.createQuery("from theme"); // why not mapped
//cant use .list()?
session.getTransaction().commit();
System.out.println("query " + query);
} catch (Exception e) {
e.printStackTrace();
}finally{
session.close();
}
return obsList; // nothing at the moment
hibernate.cfg.xml
<!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>
<property name="connection.driver_class">org.sqlite.JDBC</property>
<!-- I know sqlite with an mysql dialect isnt the best, in the future it
will be mySql--> <property
name="connection.url">jdbc:sqlite:C:\\DokiDB\\database.db</property>
<property name="connection.username">progdm</property>
<property name="connection.password">release</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.pool_size">1</property>
<property name="show_sql">true</property>
<property name="current_session_context_class">thread</property>
</session-factory>
</hibernate-configuration>
mapped class
#Entity
#Table(name="theme")
public class mapTheme {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="theme_id")
private int theme_id;
#Column(name="theme_name")
private String theme_name;
public int getTheme_id() {
return theme_id;
}
public void setTheme_id(int theme_id) {
this.theme_id = theme_id;
}
public String getTheme_name() {
return theme_name;
}
public void setTheme_name(String theme_name) {
this.theme_name = theme_name;
}
public mapTheme(String theme_name) {
super();
this.theme_name = theme_name;
}
public mapTheme(){
}
}
what can I do?
The table name used in the query is not the name of the table in database, it should be the name of the entity class.
In this case you must use
Query query = session.createQuery("from mapTheme");
instead of :
Query query = session.createQuery("from theme")

Use custom creds while using DropWizard JDBI

I'm developing a webservice using Dropwizard JDBI framework.
Now, instead of having a db configurations in yaml file, I want to use 'user specified params' what i mean to say is, the db configs will be provided through the endpoint url.
Is having custom creds possible through dropwizard jdbi?
if yes, what changes should i be thinking to do in the code while referring this ? ->
http://dropwizard.readthedocs.org/en/latest/manual/jdbi.html
I understand, in normal flow, the service method gets the config details in the run method -
-- Config Class
public class ExampleConfiguration extends Configuration {
#Valid
#NotNull
#JsonProperty
private DatabaseConfiguration database = new DatabaseConfiguration();
public DatabaseConfiguration getDatabaseConfiguration() {
return database;
}
}
-- Service Class
#Override
public void run(ExampleConfiguration config,
Environment environment) throws ClassNotFoundException {
final DBIFactory factory = new DBIFactory();
final DBI jdbi = factory.build(environment, config.getDatabaseConfiguration(), "postgresql");
final UserDAO dao = jdbi.onDemand(UserDAO.class);
environment.addResource(new UserResource(dao));
}
-- and yaml
database:
# the name of your JDBC driver
driverClass: org.postgresql.Driver
# the username
user: pg-user
# the password
password: iAMs00perSecrEET
# the JDBC URL
url: jdbc:postgresql://db.example.com/db-prod
But in this case, I might get the config details in the Resource level...
smthing like -
#GET
#Path(value = "/getProduct/{Id}/{dbUrl}/{dbUname}/{dbPass}")
#Produces(MediaType.APPLICATION_JSON)
public Product getProductById(#PathParam(value = "Id") int Id,
#PathParam(value = "dbUrl") String dbUrl,
#PathParam(value = "dbUname") String dbUname,
#PathParam(value = "dbPath") String dbPass) {
//I have to connect to the DB here! using the params i have.
return new Product(); //should return the Product
}
I'd appreciate if someone can point me a direction.
Why not just use JDBI directly?
#GET
#Path(value = "/getProduct/{Id}/{dbUrl}/{dbUname}/{dbPass}")
#Produces(MediaType.APPLICATION_JSON)
public Product getProductById(#PathParam(value = "Id") int id,
#PathParam(value = "dbUrl") String dbUrl,
#PathParam(value = "dbUname") String dbUname,
#PathParam(value = "dbPass") String dbPass) {
DataSource ds = JdbcConnectionPool.create(dbUrl, dbUname, dbPass);
DBI dbi = new DBI(ds);
ProductDAO dao = dbi.open(ProductDao.class);
Product product = dao.findById(id);
dao.close();
ds.dispose();
return product;
}
#RegisterMapper(ProductMapper.class)
static interface ProductDao {
#SqlQuery("select id from product_table where id = :id") // Whatever SQL query you need to product the product
Product findById(#Bind("id") int id);
#SqlQuery("select * from product_table")
Iterator<Product> findAllProducts();
}
static class ProductMapper implements ResultSetMapper<Product> {
public Product map(int index, ResultSet r, StatementContext ctx) throws SQLException {
return new Product(r.getInt("id")); // Whatever product constructor you need
}
}
There's a notion in the spring world of using a database router (reference: https://spring.io/blog/2007/01/23/dynamic-datasource-routing/).
You likely could setup a proxy for the database connection factory passed to DBI. That proxy would then get the credentials from a thread local (perhaps) and return the real connection giving you what you're after and still let you use the run type proxies.

SpringPwdDecrypter how to call it from spring framework as per below

I have the following in my beans spring XML configuration file. here they are trying to get the password from a properties file run time and decrypt the password by calling the class "SpringPwdDecrypterUtil" which extends spring FactoryBean.
Question: in properties file where they mentioned with user and password. But the password is decrypted one which will be decrpted by the below configuration. I want to know how they(existing prod applicaiton) would have generated the password(decrpyted) and placed in the text file !
bean.xml
<property name="password">
<bean class="com.cname.SpringPwdDecrypterUtil">
<property name="password">
<value>${db.password}</value>
</property>
</bean>
</property>
--SpringPwdDecrypterUtil.java
package com.cname;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.FactoryBean;
public class SpringPwdDecrypterUtil
implements FactoryBean
{
private String password = null;
private String key = null;
public void setPassword(String password) {
this.password = password;
}
public void setKey(String key) {
this.key = key;
}
public Object getObject() {
if (StringUtils.isNotEmpty(this.key)) {
return CryptoUtils.decryptAndEncodeBase64(this.key, this.password);
}
return CryptoUtils.decryptAndEncodeBase64(this.password);
}
public Class getObjectType() {
return String.class;
}
public boolean isSingleton() {
return true;
}
}

Categories