《关于MySQL8.0.11连接JDBC代码及连接Hibernate-5.3.1.Final时Hibernate.cfg.xml文件的写法遇到的问题及解决过程》
(1)OS:Window7 sp1 x64
(2)JDK:jdk-10.0.0.2
(3)Hibernate:5.3.4 Final
(4)MySQL:8.0.11
(1)通过JDBC连接MySQL。主要用于验证连接、访问MySQL数据库的各个设置的正确性,在通过Hibernate连接MySQL时则可以排除连接参数设定错误。
(2)实现通过Hibernate访问MySQL。测试验证连接MySQL时如何通过文件配置Hibernate,为Hibernate集成到其他框架(如SSH)里提供参考设定。
(1)创建项目。新建Maven项目hibernate_test, archetype 选择【Maven- archetype -quickstart】。
(2)配置日志。在项目【src/main/resources】中添加日志显示配置文件log4j.properties,设置信息显示级别为DEBUG,便于分析程序的工作过程。代码内容如下。
log4j.rootLogger=DEBUG,ACT
log4j.appender.ACT=org.apache.log4j.ConsoleAppender
log4j.appender.ACT.layout=org.apache.log4j.PatternLayout
log4j.appender.ACT.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
(3)配置pom.xml文件,导入必要的包文件。文件内容如下。
4.0.0
com.seu.liuds
hibernate_test
0.0.1-SNAPSHOT
jar
hibernate_test
http://maven.apache.org
UTF-8
10.0.0.2
junit
junit
4.10
test
mysql
mysql-connector-java
8.0.11
c3p0
c3p0
0.9.1.2
org.hibernate
hibernate-c3p0
5.3.4.Final
com.mchange
mchange-commons-java
0.2.15
org.hibernate
hibernate-core
5.3.4.Final
javax.xml.bind
jaxb-api
2.3.0
com.sun.xml.bind
jaxb-impl
2.3.0
com.sun.xml.bind
jaxb-core
2.3.0
log4j
log4j
1.2.17
(4)在MySQL中新建/选择数据库作为连接对象,此处选择【sshdemo】,并在数据库sshdemo中建立一个表格user。本例子采用MySQL Workbench工具完成数据库和表格的创建。
(5)创建包含main()方法的类JDBCTest,包名选择【com.seu.liuds.hibernate_test.controller】,用于实现与MySQL的连接测试。代码如下。
package com.seu.liuds.hibernate_test.controller;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCTest {
// JDBC驱动名,MySQL6以后的JDBC驱动
static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
// 数据库 URL,必須手动禁用SSL且设定时区,此处设定为东8区
static final String DB_URL = "jdbc:mysql://localhost:3306/sshdemo?useSSL=false&serverTimezone=GMT%2B8";
// 数据库的用户名与密码,需要根据自己的设置
static final String USER = "root";
static final String PASS = "";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
System.out.println("Connect to database...");
// 注册JDBC驱动,
Class.forName(JDBC_DRIVER);
System.out.println("URL = " + DB_URL + "\nUSR = " + USER + "\nPWD = " + PASS);
// 创建连接
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
// 定义SQL语句,刪除id = 1的数据项
String sql = "delete from user where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
// 执行SQL语句
int count = ps.executeUpdate();
System.out.println(count);
// 提交事务
conn.commit();
} catch (Exception e) {
// 回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} finally {
// 关闭资源
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
执行JDBCTest.java,运行结果如下:
Connect to database...
URL = jdbc:mysql://localhost:3306/sshdemo?useSSL=false&serverTimezone=GMT%2B8
USR = root
PWD =
0
上述结果说明,MySQL数据库工作正常,通过JDBC连接MySQL的参数设置正确,即
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.db.url = jdbc:mysql://localhost:3306/sshdemo?useSSL=false&serverTimezone=GMT%2B8
jdbc.user = root
jdbc.password =
Hibernate是一个开源的对象/关系映射(ORM)框架,它对JDBC进行了轻量级的封装,借助它可以使用面向对象的方法来操作和使用数据库。通过Hibernate连接MySQL具体包括以下四个步骤:
(1)配置Hibernate;
(2)创建持久化类;
(3)创建持久化类的映射配置文件;
(4)通过Hibernate API操作数据库。
下面分别阐述。
关于Hibernate的配置问题,《Hibernate的配置详解》阐释的比较清楚,主要完成以下三项工作:
(1)加载数据库相关信息
(2)Hibernate的相关配置
(3)加载映射配置文件
这里(1)(2)项采用hibernate.properties文件设定,存放在src/main/resources目录中,代码如下。
# MySQL JDBC information
hibernate.connection.driver_class = com.mysql.cj.jdbc.Driver
hibernate.connection.url = jdbc:mysql://localhost:3306/sshdemo?useSSL=false&serverTimezone=GMT%2B8
hibernate.connection.username = root
hibernate.connection.password =
# Hibernate configuration
hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
hibernate.hbm2ddl.auto = update
hibernate.show_sql = false
hibernate.format_sql = true
connection.pool_size = 20
hibernate.current_session_context_class = thread
# Hibernate for c3p0
hibernate.connection.provider_class = org.hibernate.c3p0.internal.C3P0ConnectionProvider
hibernate.c3p0.max_size = 20
hibernate.c3p0.min_size = 5
hibernate.c3p0.timeout = 120
automaticTestTable = Test
hibernate.c3p0.max_statements = 100
hibernate.c3p0.acquire_increment = 1
c3p0.testConnectionOnCheckout = true
c3p0.idleConnectionTestPeriod = 18000
c3p0.idle_test_period = 120
如果希望在控制台查看Hibernate输出的MySQL语句,可设置【hibernate.show_sql = true】。针对C3P0的设置可以删除,在一般开发环境下看不出有太多影响。
(3)加载映射配置文件项采用hibernate.cfg.xml文件设定。
因为在Eclipse中开发,所以hibernate.cfg.xml存放在src/main/resources/目录下,内容入下。
其中,映射配置文件的名称是:类名.hbm.xml,一般放置在实体类所在的包下,用于建立表与类之间的映射关系。此处,将建立一个名为User的持久化类,并建立映射配置文件User.hbm.xml,二者均存放在src/main/com/seu/liuds/hibernate_test/entity/目录下。
创建持久化类com.seu.liuds.hibernate_test.entity.User,里面包含一些基本的用户信息。
package com.seu.liuds.hibernate_test.entity;
import java.util.Date;
public class User {
private int id;
private String name;
private int age;
private String sex;
private Date birthday;
private String address;
public User() {
}
// 使用指定ID方式确定主键的时候,采用该方法
public User(int id, String name, int age, String sex, Date birthday, String address) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.birthday = birthday;
this.address = address;
}
// 使用非指定ID方式确定主键的时候,采用该方法
public User(String name, int age, String sex, Date birthday, String address) {
this.name = name;
this.age = age;
this.sex = sex;
this.birthday = birthday;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", birthday=" + birthday
+ ", address=" + address + "]";
}
}
后续可以考虑采用@Data注解创建类中的主要方法,此处略过。
映射配置文件的主要作用就是将持久化类与数据库表进行映射。这里将User类映射为一个名为user的表,将对象的属性映射为数据库的列,将属性的类型映射为数据库列的类型。
在项目中的src/test/java中创建测试程序com.seu.liuds.hibernate_test.UserTest。具体操作包括:
(1)连接MySQL数据库;
(2)删除一条记录;
(3)关闭连接。
package com.seu.liuds.hibernate_test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.seu.liuds.hibernate_test.entity.User;
public class UserTest {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init() {
System.out.println("Initialization");
//创建配置对象
Configuration config = new Configuration().configure();
//创建会话工厂对象
sessionFactory = config.buildSessionFactory();
//创建会话对象
session = sessionFactory.openSession();
//开启事务
transaction = session.beginTransaction();
}
@After
public void destory() {
System.out.println("Test End.");
//提交事务
transaction.commit();
//关闭会话
session.close();
//关闭会话工厂
sessionFactory.close();
}
@Test
public void saveNewUser() throws Exception{
System.out.println("Testing...");
User usr = new User("赵六", 30, "男", new Date(), "北京市");
session.save(usr);
System.out.println("Current ID is: "+ usr.getId());
}
}
将日志显示级别设定为ERROR时,运行结果如下:
Initialization
Testing...
Current ID is: 5
Test End.
Current ID为当前user表中最大id序号,每执行一次,新增一条记录,该值自动加1。显然,通过Hibernate连接并操作MySQL数据库成功。
将日志显示级别设定为DEBUG时,会出现如下调试信息,虽然不影响程序运行和功能实现,总觉得心里梗梗的,查了大量的中外网站,也没有找到清除这些信息的方法,希望有高手指点一二,拜谢!
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.sun.xml.bind.v2.runtime.reflect.opt.Injector (file:/D:/maven_repo/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)
WARNING: Please consider reporting this to the maintainers of com.sun.xml.bind.v2.runtime.reflect.opt.Injector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Oracle官方博客在《WebLogic Server and Java SE 9》一文中给出如下解释:
The illegal reflective access warnings are coming from code that has been updated to work on JDK9 but the logic checks if the JDK8 approach works first. The warnings will go away when the default is (or you run explicitly with) --illegal-access=deny.
言下之意应该是代码运行在JDK9上,而逻辑检查首先查找JDK8的路径。可以参考《非法反射 An illegal reflective access operation has occurred》一文。目前只能置之不理。
03:14:30,882 [main] DEBUG com.mchange.v2.c3p0.impl.NewPooledConnection - com.mchange.v2.c3p0.impl.NewPooledConnection@78a0ff63 closed by a client.
java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:566)
……
网上一篇比较深入分析的文章《java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE 的理解》显示,经过分析源码,得到的结论是这个异常可以无视掉。而在《DEBUG STACK TRACE for PoolBackedDataSource.close().》中,则认为这是可以通过Spring设定来解决的。为此,专门研读了《Hibernate配置C3P0连接池》了一文,待研究SSH时验证此法是否可行。
03:14:31,139 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl - Unable to resolve connection default schema
org.hibernate.HibernateException: Use of DefaultSchemaNameResolver requires Dialect to provide the proper SQL statement/command but provided Dialect [org.hibernate.dialect.MySQL8Dialect] did not return anything from Dialect#getCurrentSchemaCommand
at org.hibernate.engine.jdbc.env.internal.DefaultSchemaNameResolver$SchemaNameResolverFallbackDelegate.resolveSchemaName(DefaultSchemaNameResolver.java:100)
……
信息显示:无法解析默认的连接模式。原因是设置的MySQL8Dialect没有给DefaultSchemaNameResolver提供必需的SQL声明或者命令。但是,程序的执行并没有收到影响,连接和访问数据库都正常,这个问题怎么破?
本文成文过程中,参考了诸多高手的文章,除了在正文中引用的文章以外,还包括以下文章,在此一并感谢。
(1)CSDN博主李阿昀的关于Hibernate框架的系列文章
(2)博园网博主酒皇关于Hibernate的系列文章
(3)《记更新MySQL 8.0后踩过的那些坑》
(4)《总结SSH阶段常见的异常系列之一hibernate》
(5)《Hibernate学习笔记(一)——简单的Hibernate实例入门》
(6)《关于Hibernate 5 和 Hibernate 4 在创建SessionFactory的不同点分析(解决 org.hibernate.MappingException: Unknown entity: xx类报错问题)》
(7)《Javaweb或javaEE完整项目名及包名、资源名命名规则》
(8)《如何去除get,set方法。@Data注解和如何使用,lombok》
(9)《Reducing Boilerplate Code with Project Lombok》