那些走过的坑

走过hibernate的坑。。。

今天搞了一下hibernate,手动导的jar包,手动配置的文件,结果。。呵呵,一大堆的问题,在此记录下来,避免再入坑。。

1、导包

1)、导入hibernate相关包

官网下载hibernate

下载好后是这样的:

将lib/required下的所有包导入项目中。

2)、导入mysql驱动

我用的是mysql数据库,所以别忘了mysql驱动,下载地址:https://dev.mysql.com/downloads/connector/j/

找到mysql-connector-java-xxx.jar下载下来就好了。将驱动导入项目中。

2、建立hibernate配置文件

可以在hibernate-xxx/project/etc/下找到hibernate.cfg.xml文件

复制到项目类路径下,现在项目结构是这样的:

3、建立相关的java类

代码如下:

1)、建立实体类

User类

package com.miner.usermgr.domain;

public class User {

    private Integer id;
    private String name;

    public User(){

    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2)、建立映射文件

即告诉hibernate将实体类映射到数据库中的哪个表,以及类中的哪个属性对应到数据库中的哪个字段。

User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping package="com.miner.usermgr.domain">
    <class name="User" table="user">
        <id name="id" type="java.lang.Integer">
            <generator class="native" />
        </id>
        <property name="name" column="name" type="java.lang.String" />
    </class>
</hibernate-mapping>

3)、建立manager

UserManager接口

package com.miner.usermgr.manager;

import com.miner.usermgr.domain.User;

public interface UserManager {

    public void addUser(User user);
}

4)、建立获取hibernate的SessionFactory的类

HibernateSessionFactory.java

package com.miner.usermgr.util;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateSessionFactory {

    //指定hibernate配置文件路径
    private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
    //创建ThreadLocal对象
    private static final ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<Session>();
    //创建Configuration对象
    private static Configuration configuration = new Configuration();
    //定义SessionFactory对象
    private static SessionFactory sessionFactory;
    //定义configFile属性并赋值
    private static String configFile = CONFIG_FILE_LOCATION;

    static{
        try{
            //读取配置文件hibernate.cfg.xml
            configuration.configure();
            //生成一个注册机对象
            ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
            //使用注册机对象serviceRegistry创建sessionFactory
            sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        }catch(HibernateException e){
            e.printStackTrace();
        }
    }

    //创建无参的HibernateSessionFactory方法
    private HibernateSessionFactory(){ }

    //获得sessionFactory对象
    public static SessionFactory getSessionFactory(){
        return sessionFactory;
    }

    //重建sessionFactory
    public static void rebuildSessionFactory(){
        synchronized(sessionFactory){
            try{
                configuration.configure(configFile);
                ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
                //使用注册机对象serviceRegistry创建sessionFactory
                sessionFactory = configuration.buildSessionFactory();
            }catch(HibernateException e){
                e.printStackTrace();
            }
        }
    }

    //获得Session对象
    public static Session getSession(){
        //获得ThreadLocal对象管理的Session对象
        Session session = (Session)sessionThreadLocal.get();
        try{
            //判断Session对象是否已经存在或打开
            if(session == null || !session.isOpen()){
                //如果Session对象为空或未打开,再判断sessionFactory对象是否为空
                if(sessionFactory == null){
                    //如果sessionFactory为空,则创建sessionFactory
                    rebuildSessionFactory();
                }
                //如果sessionFactory不为空,则打开Session
                session = (sessionFactory != null)? sessionFactory.openSession():null;
                sessionThreadLocal.set(session);
            }
        }catch(HibernateException e){
            e.printStackTrace();
        }
        return session;
    }

    //关闭Session对象
    public static void closeSession(){
        Session session = (Session)sessionThreadLocal.get();
        sessionThreadLocal.set(null);
        try{
            if(session != null && session.isOpen()){
                session.close();
            }
        }catch(HibernateException e){
            e.printStackTrace();
        }
    }

    //configFile属性的set方法
    public static void setConfigFile(String configFile){
        HibernateSessionFactory.configFile = configFile;
        sessionFactory = null;
    }

    //configuration属性的get方法
    public static Configuration getConfiguration(){
        return configuration;
    }
}

5)、建立manager实现类

UserManagerImpl类

package com.miner.usermgr.manager;

import com.miner.usermgr.domain.User;
import com.miner.usermgr.util.HibernateSessionFactory;
import com.miner.usermgr.util.HibernateUtils;
import org.hibernate.Session;
import org.hibernate.Transaction;

import java.util.Date;

public class UserManagerImpl implements UserManager {

    public void addUser(User user){
        Session session = null;
        Transaction tx = null;
        try{
            session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
            tx = session.beginTransaction();
            session.save(user);
            tx.commit();
        }catch(Exception e){
            if(tx != null){
                tx.rollback();
            }
            e.printStackTrace();
        }finally{
            HibernateSessionFactory.closeSession();
        }
    }
}

4、配置hibernate.cfg.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!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="show_sql">true</property>
		<property name="connection.url">jdbc:mysql://localhost:3306/spring_hibernate_1?useSSL=false</property>
		<property name="connection.username">root</property>
		<property name="connection.password">root</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="hbm2ddl.auto">update</property>
		<mapping resource="com/miner/usermgr/domain/User.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

5、编写测试类

package test;

import com.miner.usermgr.domain.User;
import com.miner.usermgr.manager.UserManager;
import com.miner.usermgr.manager.UserManagerImpl;
import junit.framework.TestCase;

public class TestUserMgr extends TestCase {

    public void testAddUser(){
        UserManager userManager = new UserManagerImpl();
        User user = new User();
        user.setName("张三");
        userManager.addUser(user);
    }
}

6、各种bug

好啦,写完了总该运行吧,于是兴奋地点击Run……然后,报错了。。。

[v_error]问题一:org.hibernate.MappingException: Unknown entity[/v_error]

然后开启了debug。。。发现session始终是null。。。心里:???再三检查发现代码并没有写错。

分析:

报错的可能原因有两种:

  1. 实体类并没有映射到数据库,也就是没有映射正确;
  2. 获取sessionFactory的方式有问题。

经过检查,实体类确实映射得没有问题,那就是出在获取sessionFactory的地方了。查过资料后发现:

[v_tips]

hibernate4.2之前:

//创建配置对象
Configuration config = new Configuration().configure();
//创建服务注册对象
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties ()).buildServiceRegistry();
//创建会话工厂对象
sessionFactory = config.buildSessionFactory(serviceRegistry);

4.3之后上面的ServiceRegistryBuilder类被弃用。

 

hibernate4.35之前SessionFactory的获取方式:

// 创建配置对象
Configuration config = new Configuration().configure();
// 创建服务注册对象(hibernate4.35之后该方法就不能再获取到实体信息了)
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().
applySettings(conf.getProperties()).build();
// 创建会话工厂对象
sessionFactory = config.buildSessionFactory(serviceRegistry);

 

Hibernate4.35之后SessionFactory的获取方式:

1、

 

// 创建会话工厂对象
sessionFactory = new Configuration().configure().buildSessionFactory();

2、

StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder().configure().build();

Metadata metadata = new MetadataSources(standardRegistry).getMetadataBuilder()
.applyImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE).build();

SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();

[/v_tips]

解决办法:

我用的是hibernate是5.0.2版本的,也就是说需要修改获得SessionFactory的方法。

将HibernateSessionFactory.java改为如下:

package com.miner.usermgr.util;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateSessionFactory {

    private static final ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<Session>();

    private static SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (HibernateException e) {
            e.printStackTrace();
        }
    }

    //创建无参的HibernateSessionFactory方法
    private HibernateSessionFactory(){ }

    //获得sessionFactory对象
    public static SessionFactory getSessionFactory(){
        return sessionFactory;
    }

    //重建sessionFactory
    public static void rebuildSessionFactory(){
        synchronized(sessionFactory){
            try{
                sessionFactory = new Configuration().configure().buildSessionFactory();
            }catch(HibernateException e){
                e.printStackTrace();
            }
        }
    }

    //获得Session对象
    public static Session getSession(){
        //获得ThreadLocal对象管理的Session对象
        Session session = (Session)sessionThreadLocal.get();
        try{
            //判断Session对象是否已经存在或打开
            if(session == null || !session.isOpen()){
                //如果Session对象为空或未打开,再判断sessionFactory对象是否为空
                if(sessionFactory == null){
                    //如果sessionFactory为空,则创建sessionFactory
                    rebuildSessionFactory();
                }
                //如果sessionFactory不为空,则打开Session
                session = (sessionFactory != null)? sessionFactory.openSession():null;
                sessionThreadLocal.set(session);
            }
        }catch(HibernateException e){
            e.printStackTrace();
        }
        return session;
    }

    //关闭Session对象
    public static void closeSession(){
        Session session = (Session)sessionThreadLocal.get();
        sessionThreadLocal.set(null);
        try{
            if(session != null && session.isOpen()){
                session.close();
            }
        }catch(HibernateException e){
            e.printStackTrace();
        }
    }
}

或者简单改简单一点,像这样:

package com.miner.usermgr.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

    private static SessionFactory factory;

    private HibernateUtils() {
    }

    static {
        try {
            factory = new Configuration().configure().buildSessionFactory();
        }catch(Exception e) {
            e.printStackTrace();
            throw new java.lang.RuntimeException(e);
        }
    }

    public static SessionFactory getSessionFactory() {
        return factory;
    }

    public static Session getSession() {
        return factory.openSession();
    }

    public static void closeSession(Session session) {
        if (session != null) {
            if (session.isOpen()) {
                session.close();
            }
        }
    }
}

ok,改完,再运行,还是报错。。。

[v_error]问题二:Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘type=MyISAM’ at line 1[/v_error]

分析:

  1. 首先检查数据库,看看表是否成功建立,答案是并没有,spring_hibernate_1下没有表;
  2. 根据报错信息猜测问题出在type=MyISAM上。

经查资料发现:

type=MyISAM 是在Mysql 4.x 版本时创建表时用的语法,5.5后的版本将该语法删除,改为engine=MyISAM。只需要修改为engine=MyISAM即可,但现在我们是让hibernate替我们建表,
而我在hibernate.cfg.xml中,配置的是下面的值:
<property name=”dialect”>org.hibernate.dialect.MySQLDialect</property>

于是hibernate会使用MySQL 4.x 的sql生成工具,但我连接的数据库是 MySQL 5.7的 , 所以会导致生成的sql有语法错误。

解决办法:

将配置文件中的dialect改为:

<property name=”dialect”>org.hibernate.dialect.MySQL5Dialect</property>

技巧:

在方言中间加上数据库版本。

 

好了现在可以正常插入数据了,但还是报了个异常:

[v_error]问题三:org.hibernate.HibernateException: No CurrentSessionContext configured!
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:462)[/v_error]

分析:

sessionFactory.openSession()在任何情况都会重新开启一个Session,而getCurrentSession()增加了一个判断,在有Session的情况下就会直接去调用,没有session的话才会创建。

查知,在使用currentSession()的时候要在hibernate.cfg.xml中进行相关的事务配置。
在全局环境下(例如Tomcat),要在hibernate.cfg.xml中加入:

<property name=“current_session_context_class”>jta</property>

在本地环境下,要在hibernate.cfg.xml中加入:

<property name=“current_session_context_class”>thread</property>

解决办法:

因为我们这里并没有使用到tomcat服务器,所以在hibernate.cfg.xml中加入:

<property name=“current_session_context_class”>thread</property>

 

 

附录:

hibernate建表方言

DB2 org.hibernate.dialect.DB2Dialect
DB2 AS/400 org.hibernate.dialect.DB2400Dialect
DB2 OS390 org.hibernate.dialect.DB2390Dialect
PostgreSQL org.hibernate.dialect.PostgreSQLDialect
MySQL org.hibernate.dialect.MySQLDialect
MySQL with InnoDB org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version) org.hibernate.dialect.OracleDialect
Oracle 9i/10g org.hibernate.dialect.Oracle9Dialect
Sybase org.hibernate.dialect.SybaseDialect
Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server org.hibernate.dialect.SQLServerDialect
SAP DB org.hibernate.dialect.SAPDBDialect
Informix org.hibernate.dialect.InformixDialect
HypersonicSQL org.hibernate.dialect.HSQLDialect
Ingres org.hibernate.dialect.IngresDialect
Progress org.hibernate.dialect.ProgressDialect
Mckoi SQL org.hibernate.dialect.MckoiDialect
Interbase org.hibernate.dialect.InterbaseDialect
Pointbase org.hibernate.dialect.PointbaseDialect
FrontBase org.hibernate.dialect.FrontbaseDialect
Firebird org.hibernate.dialect.FirebirdDialect

 

 

 

 

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注