Java for Web学习笔记(九八):持久化初探(3)JPA小例子(上)

在tomcat中配置

我们采用数据源在tomcat中配置的方式:

  1. 数据库的配置在war之外。
    • 在很多网上资料中,这是设置在web app里面的xml中,这存在一个问题,如果配置封装在war中,当我们部署环境出现变化,最简单地,ip地址变更,账号密码变更,就需要重新封装war,当然,我们可以手动更改tomcat解析后的war包里面的配置,但对于升级和版本回退存在问题。最好的方式是保持war不变,部署环境的变化在非war包封装的配置文件中进行。
  2. 我们使用了tomcat来管理连接,这样就无需引入C3P0作为连接池管理。
    • 引入的包总是越少越好。
    • 我们之前是基于每个web app使用JDBC的,见C3P0例子,我们需要在web app卸装的时候(web app运行在container之中,不是进程),小心进行deregisterDriver,否则会引发内存泄漏的问题。将数据库连接管理池放置在Tomcat,由tomcat进行统一进行管理,Driver对所有的web app有效。
  3. 在web app中,我们无需再引入mysql-connetor的jar包,通过JPA解耦底层具体的数据库实现,便于数据库的移植。

步骤1:在tomcat中加入Mysql connector/J

从Mysql中下载 Connector/J,我们使用5.1.44版本,并将其放入tomcat/lib下,这样,在下次启动tomcat时,这个lib有效。

mysql connector的版本似乎有点怪,同时维护了3个版本。貌似6似一个过渡版本,不喜欢7,8正在开发过程。如果我们选择了6版本,package的名字有所变化,Driver为com.mysql.cj.jdbc.Driver。在我的测试中,tomcat版本为8.0.36,如果使用6的版本,在web卸载是会报告:

十一月 21, 2017 11:00:21 上午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads警告: The web application [chapter20] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
  java.lang.Object.wait(Native Method)
  java.lang.ref.ReferenceQueue.remove(Unknown Source)
  com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)

关于AbandonedConnectionCleanupThread的异常是可以代码修复的,如下:

AbandonedConnectionCleanupThread.checkedShutdown(); //mysql connector 5版本在web app卸载时执行
AbandonedConnectionCleanupThread.shutdown(); //mysql connector 6版本在web app卸载时执行
但是我们已经引入的JPA作为中间层,为什么还要去调用底层mysql-connector.jar包的接口(需要在app中加入该jar)。而使用mysql-connector 5.1.44,不会出现这个异常。这也可能是因为我的tomcat版本过低问题。我们在后面的学习中,都将使用5.1.44版本。

步骤2:在tomcat中设置datasource

打开tomcat/conf/context.xml,添加下面的内容

<Resource name="jdbc/DataSourceName" type="javax.sql.DataSource"
  maxActive="20" maxIdle="5" maxWait="10000"
  username="mysqluser" password="mysqlpassword"
  driverClassName="com.mysql.jdbc.Driver"
  url="jdbcUrl" />
如果有多个数据库,则添加多个。具体例子如下,我们还需要设置SSL,autoReconnect,以及传输的字符集,&需要进行转义为&amp;,否则tomcat在启动时会报错。对于开发环境,context.xml位于我们的workspace下,如<workspace>\Servers\Tomcat v8.0 Server at localhost-config
<Resource name="jdbc/learnTest" type="javax.sql.DataSource"
	maxTotal="20" maxIdle="5" maxWaitMillis="10000"
	username="test" password="test123456"
	driverClassName="com.mysql.jdbc.Driver"
	url="jdbc:mysql://191.8.1.107:3306/test?useSSL=true&amp;autoReconnect=true&amp;useUnicode=true" />

useSSL建议打开,否则明文传递,会给出告警

Thu Nov 23 08:17:53 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

在C3P0中,我们需要设置autoReconnect=true,以确保数据库连接池中连接的有效,不会因为长时间没有访问,导致连接断开失效。现在我们使用的是tomcat对连接的管理,我们测试发现:

如果长时间没有访问,在新的访问时,tomcat会重新建立一条连接,JPA进行Entity的映射,这和C3P0不同。虽然建立连接及登录过程需要一点时间,但若长时间没有访问,说明此时性能要求很低,这点时间是可以容忍的,因此autoReconnect此项设置并不重要,可以jdbc:mysql://191.8.1.107:3306/test?useSSL=true。当然,安全地,设上autoReconnect=true是保险的做法。

pom.xml

我们需要在pom.xml中增加以下内容

<!-- JPA,版本为2.2,在maven中心,没有什么官方的JPA接口,一般会使用eclipse提供的(eclipse link是JPA的范例,虽然hibernate更流行。目前提供了2.2版本,但我们仍使用2.1版本,因为Hibernate ORM的5.2.12.Final只支持2.1版本(hibernate-jpa-2.1-api)。 -->
<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>javax.persistence</artifactId>
    <version>2.1.0</version>
    <scope>compile</scope>
</dependency>	
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>javax.transaction-api</artifactId>
    <version>1.2</version>
    <scope>compile</scope>
</dependency>
<!-- Hibernate ORM,我们不使用非JPA的私有部分,不直接调用其接口,因此用runtime即可。artifactId也可以使用hibernate-entitymanager -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.2.12.Final</version>
    <scope>runtime</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.jboss.spec.javax.transaction</groupId>
            <artifactId>jboss-transaction-api_1.2_spec</artifactId>
            </exclusion>
        <exclusion>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

小例子的数据库

一些sql语句

在给出数据表格schema之前,我们先看看一些SQL的用法。

常用字符集和collate
utf8_unicode_ci 大小写不敏感。_ci(大小写不敏感)、_cs(大小写敏感)或_bin(binary)。
CREATE DATABASE EntityMappings DEFAULT CHARACTER SET 'utf8' DEFAULT COLLATE 'utf8_unicode_ci';

常用的字符集utf8,collate为utf8_unicode_ci,即对与名字采用大小写不敏感方式,很多name,email等会采用不敏感方式。

插入前触发
在插入表A前先触发在表B中执行某条SQL语句,下面例子表示在将数据加入表A个前,先执行:insert into B values('1','building');
delimiter $$   -- 一般使用$$,虽然分界符可以用其他,例如$,因为里面可能涉及多个SQL语句,含有缺省的分界符号;,会引发语义混淆
create trigger A_trigger before insert on A for each row 
   begin
       insert into B values('1','building');
   end;
$$
DELIMITER; -- 将分界符改回来,否要要使用show tables;$$

介绍触发,是因为小例子给出了generator,其本质就是插入前触发。

小例子的表格的schema

-- 表:作者
CREATE TABLE `Authors` (
  `AuthorId` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `AuthorName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `EmailAddress` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`AuthorId`),
  KEY `Author_Names` (`AuthorName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 表:书
CREATE TABLE `Books` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `Isbn` varchar(13) COLLATE utf8_unicode_ci NOT NULL,
  `Title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `Author` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `Price` decimal(6,2) NOT NULL,
  `Publisher` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Books_ISBNs` (`Isbn`),
  KEY `Books_Titles` (`Title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 表:出版商,和前面两个表不同,这里的Id没有使用AUTO_INCREMENT,而是通过另一个表格SurrogateKeys中的KeyValue的值获取。这是对历史项目的一个模拟,已经很少使用了。
CREATE TABLE `Publishers` (
  `PublisherId` bigint(20) unsigned NOT NULL,
  `PublisherName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `Address` varchar(1024) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`PublisherId`),
  KEY `Publishers_Names` (`PublisherName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 表:这是个辅助Publishers表格生产PublisherId的表格。
CREATE TABLE `SurrogateKeys` (
  `TableName` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `KeyValue` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`TableName`),
  KEY `SurrogateKeys_Table_Values` (`TableName`,`KeyValue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

相关链接: 我的Professional Java for Web Applications相关文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值