欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Hibernate入门这一篇就够了

程序员文章站 2022-06-15 10:37:39
历史就是一面镜子 回顾自己开发的历程,见证了时代变迁史记,下面我针对java连接数据库的方式说起 0 原生jdbc 先普及下jdbc,怕新入行的人早已沉浸在包装库和框架中,甚至都没用过原生jdbc。 Java数据库连接,全称是Java Database Connectivity,简称JDBC,是Ja ......

历史就是一面镜子

回顾自己开发的历程,见证了时代变迁史记,下面我针对java连接数据库的方式说起

0 原生jdbc

先普及下jdbc,怕新入行的人早已沉浸在包装库和框架中,甚至都没用过原生jdbc。

java数据库连接,全称是java database connectivity,简称jdbc,是java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。jdbc也是sun microsystems的商标。我们通常说的jdbc是面向关系型数据库的。百科这样解释的java数据库连接

开发人员要做的几个步骤:

开发步骤:

  1、注册驱动.,告知jvm使用的是哪一个数据库的驱动

  2、获得连接.,使用jdbc中的类,完成对mysql数据库的连接

  3、获得语句执行平台,通过连接对象获取对sql语句的执行者对象

  4、执行sql语句,使用执行者对象,向数据库执行sql语句  获取到数据库的执行后的结果

  5、处理结果

  6、释放资源.

注意写代码之前,要导入数据库驱动包,连接不同厂商的数据库要用不同的驱动包

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

对应的驱动包 

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

新建项目,普通的java项目就行,导入第三方jar太简单了,自行百度

示例代码如下:

Hibernate入门这一篇就够了Hibernate入门这一篇就够了
import java.io.bufferedinputstream;
import java.io.fileinputstream;
import java.io.filenotfoundexception;
import java.io.ioexception;
import java.io.inputstream;
import java.sql.connection;
import java.sql.drivermanager;
import java.sql.resultset;
import java.sql.sqlexception;
import java.sql.statement;
import java.util.hashmap;
import java.util.iterator;
import java.util.properties;

import com.mysql.jdbc.resultsetmetadata;

/**
 * 
 * @author dgm
 * @describe "原生jdbc"
 * @date 2020年4月13日
 */
public class mysqltest {
    // jdbc 驱动名及数据库 url
    static final string jdbc_driver = "com.mysql.jdbc.driver";
    static final string db_url = "jdbc:mysql://192.168.8.200:3306/bdrackdemo?useunicode=true&characterencoding=utf8&autoreconnect=true";
    // 数据库的用户名与密码,需要根据自己的设置
    static final string user = "root";
    static final string pass = "cstorfs";
    static properties prop = new properties();

    //读取数据库配置文件
    static void readdbsetting(string path) {
        // properties prop = new properties();
        // 读取属性文件mysql.properties
        inputstream in = null;
        try {
            in = new bufferedinputstream(new fileinputstream(path));
        } catch (filenotfoundexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        }
        try {
            prop.load(in);
        } catch (ioexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        } // /加载属性列表
        iterator<string> it = prop.stringpropertynames().iterator();
        while (it.hasnext()) {
            string key = it.next();
            system.out.println(key + "=" + prop.getproperty(key));
        }
        try {
            in.close();
        } catch (ioexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        }
        // return prop;
    }

    public static void main(string[] args) {
        // 读取mysql 配置信息
        readdbsetting("conf/mysql.properties");

        connection conn = null;
        statement stmt = null;
        resultset rs = null;
        try {
            // 注册 jdbc 驱动
            class.forname(prop.getproperty("dbdriver")).newinstance();

            // 打开链接
            system.out.println("连接数据库...");
            conn = drivermanager
                    .getconnection(
                            "jdbc:mysql://"
                                    + prop.getproperty("mysqlhost")
                                    + ":"
                                    + prop.getproperty("mysqlport")
                                    + "/"
                                    + prop.getproperty("dbname")
                                    + "?useunicode=true&characterencoding=utf8&autoreconnect=true",
                            prop.getproperty("mysqluser"),
                            prop.getproperty("mysqlpwd"));

            // 执行查询
            system.out.println(" 实例化statement对象...");
            stmt = conn.createstatement();
            string sql = "select id, username, number from student";
            rs = stmt.executequery(sql);

            // 展开结果集数据库
            while (rs.next()) {
                // 输出数据
                system.out.print("用户id: " + rs.getint("id"));
                system.out.print(", 用户名: " + rs.getstring("username"));
                system.out.print(", 学号: " + rs.getstring("number"));
                system.out.print("\n");
            }
        } catch (sqlexception se) {
            // 处理 jdbc 错误
            se.printstacktrace();
        } catch (exception e) {
            // 处理 class.forname 错误
            e.printstacktrace();
        } finally {
            // 关闭资源
            try {
                if (rs != null)
                    rs.close();
            } catch (sqlexception se3) {
            }// 什么都不做
            // 关闭资源
            try {
                if (stmt != null)
                    stmt.close();
            } catch (sqlexception se2) {
            }// 什么都不做
            try {
                if (conn != null)
                    conn.close();
            } catch (sqlexception se) {
                se.printstacktrace();
            }
        }
    }
}

 

Hibernate入门这一篇就够了

查询结果Hibernate入门这一篇就够了Hibernate入门这一篇就够了

恩早期开发就是这么过来,只是自己封装成了jdbc工具,于具体的业务开发人员要写大量的sql语句,调用时这样的

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

 看上去此种开发模式也很好,只要规模不大,此方式还很好,瓶颈在于数据库连接,现在也不是问题了,社会在进步,池化技术出现了。

池化简单点说就是预先连接好一定数量的连接,等需要时随意从中选择一个进行操作。略了,当时主要用的tomcat自带的池化技术,此时获取数据库连接的代码就变成了这样核心伪代码(tomcat要做些配置):

//构造函数
    public dataaccess(string poolname) 
    {
        this.poolname = poolname;
        
        this.jndi = "java:comp/env/" + poolname;
    }

    //获取数据库连接
    private void setconnection() throws exception 
    {
        config config = config.getconfig();

        if ( config.getwebserver().equals("tomcat")  )
        {
            context ctx = new initialcontext();

            datasource ds = (datasource)ctx.lookup(jndi);
           
            //获取数据库连接
            this.conn = ds.getconnection();
        } else
         {
        //其他server
       }
        
    }

普及下jndihttps://baike.baidu.com/item/jndi/3792442?fr=aladdin,现在也难见到了,很多框架越来越傻瓜式了

 

1.  hibernate来了

hibernate是一种orm框架,全称为 object_relative datebase-mapping,在java对象与关系数据库之间建立某种映射,以实现直接存取java对象。

想不明白为啥没有mybatis发展的好,看来拉拢技术人员搞社区还是很重要的。

很遗憾,没有保留以前的老项目,没有自己的电脑,也没有申请github。

看了下网上已有很多教程了:,就不多说什么,重点关注架构、缓存、事务和拦截器。重点代码

  //获取加载配置管理类
        configuration configuration = new configuration();

        //不给参数就默认加载hibernate.cfg.xml文件,
        configuration.configure();

        //创建session工厂对象
        sessionfactory factory = configuration.buildsessionfactory();

        //得到session对象
        session session = factory.opensession();

        //使用hibernate操作数据库,都要开启事务,得到事务对象
        transaction transaction = session.gettransaction();

        //开启事务
        transaction.begin();

        //把对象添加到数据库中
        //session.save(user);
        //数据库操作略

        //提交事务
        transaction.commit();

        //关闭session
        session.close();

2. mybatis也来了,来的更猛

用的时间稍微长久些,没办法,谁让它势头发展的好,也有大厂强烈支持。

mybatis简介https://baike.baidu.com/item/mybatis/2824918?fr=aladdin,算是hibernate的竞争者

我看网上也有了类似教程,mybatis 教程 、

重点也是关注架构、缓存和事务

简单demo如下:

package spring.dao

public interface mybatisdao {
    list<hashmap> selectuser();
}

public class mybatisdaoimpl implements mybatisdao {

    public sqlsession sqlsession;

    public mybatisdaoimpl(sqlsession sqlsession) {
        this.sqlsession = sqlsession;
    }
    
    @override
    public list<hashmap> selectuser() {
        // todo auto-generated method stub
        return this.sqlsession.selectlist("spring.dao.mybatisdao.selectuser");
    }
}

//测试代码
public class mybatistest {

    public static void main(string[] args) {
        // todo auto-generated method stub
        try {

            system.out.println("开始mybatis实验");
            mybatisdao userdao;
            sqlsession sqlsession;

            string resource = "conf/mybatis-config.xml";
            inputstream inputstream = resources.getresourceasstream(resource);
            sqlsessionfactory sqlsessionfactory = new sqlsessionfactorybuilder()
                    .build(inputstream);
            sqlsession = sqlsessionfactory.opensession();
            userdao = new mybatisdaoimpl(sqlsession);
            list<hashmap> userlist = userdao.selectuser();
            for (hashmap user : userlist) {
                system.out.println(user);
            }
            sqlsession.close();

            system.out.println("结束mybatis实验");

        } catch (ioexception e) {
            e.printstacktrace();
        } catch (exception e) {
            e.printstacktrace();
        }
    }
}

Hibernate入门这一篇就够了

配置文件三个如下

 mybatis-config.xml

<?xml version="1.0" encoding="utf-8" ?>

<!doctype configuration
    public "-//mybatis.org//dtd config 3.0//en"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <properties resource="conf/mysql.properties" />
    
    <environments default="development">
        <environment id="development">
            <transactionmanager type="jdbc" />
            <datasource type="pooled">
                <property name="driver" value="${mybatis.driver}" />
                <property name="url" value="${mybatis.url}" />
                <property name="username" value="${mybatis.username}" />
                <property name="password" value="${mybatis.password}" />
            </datasource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="conf/mybatismapper.xml" />
    </mappers>

</configuration>
Hibernate入门这一篇就够了

mybatismapper.xml文件内容

<?xml version="1.0" encoding="utf-8" ?>
<!doctype mapper
  public "-//mybatis.org//dtd mapper 3.0//en"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper:根标签,namespace:命名空间,一般保证命名空间唯一 -->
<mapper namespace="spring.dao.mybatisdao">
   <!-- statement,内容:sql语句。id:唯一标识,在同一个命名空间下保持唯一
      resulttype:sql语句查询结果集的封装类型
    -->
   <select id="selectuser" resulttype="java.util.map">
     select id, username, number from student
   </select>
</mapper>
Hibernate入门这一篇就够了

 数据库配置mysql.properties

#mysql for mybatis
mybatis.driver=com.mysql.jdbc.driver
mybatis.url=jdbc:mysql://192.168.8.200:3306/bdrackdemo?useunicode=true&characterencoding=utf8&autoreconnect=true
mybatis.username=root
mybatis.password=cstorfs
Hibernate入门这一篇就够了

执行测试代码效果:

Hibernate入门这一篇就够了Hibernate入门这一篇就够了​ 

注意配置型的文件不能放错位置,要么就改加载属性文件的代码,记得导入数据库驱动包和mybatis.jar

 

缓存分两种,一级缓存和二级缓存。

一级缓存的几种情况:

第一种情况:同个session进行两次相同查询

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

结论:mybatis只进行1次数据库查询。

 

 第二种情况:同个session进行两次不同的查询

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

结论:mybatis进行两次数据库查询。

第三种:不同session,进行相同查询。

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

结论:mybatis进行两次数据库查询。

第四种情况:同个session,查询之后更新数据,再次查询相同的语句

直接下结论了:更新操作之后缓存会被清除

 

二级缓存

之所以称之为“二级缓存”,是相对于“一级缓存”而言的。既然有了一级缓存,那么为什么要提供二级缓存呢?我们知道,在一级缓存中,不同session进行相同sql查询的时候,是查询两次数据库的。显然这是一种浪费,既然sql查询相同,就没有必要再次查库了,直接利用缓存数据即可,这种思想就是mybatis二级缓存的初衷。

另外,spring和mybatis整合时,每次查询之后都要进行关闭sqlsession,关闭之后数据被清空。所以mybatis和spring整合之后,一级缓存是没有意义的。如果开启二级缓存,关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到mapper namespace的二级缓存中。这样,缓存在sqlsession关闭之后依然存在。

默认情况下,mybatis只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存,见: mybatis一级缓存介绍 。要启用全局的二级缓存,只需要在sql映射文件中添加一行:

<cache/>

二级缓存是mapper级别的缓存,多个sqlsession去操作同一个mapper的sql语句,多个sqlsession可以共用二级缓存,二级缓存是跨sqlsession的。

上面这个简单语句的效果如下:

映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(lru, least recently used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象的1024个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

这些属性可以通过 cache 元素的属性来修改。比如:

<cache
  eviction="fifo"
  flushinterval="60000"
  size="512"
  readonly="true"/>
Hibernate入门这一篇就够了

这个更高级的配置创建了一个 fifo 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

lru – 最近最少使用:移除最长时间不被使用的对象。
fifo – 先进先出:按对象进入缓存的顺序来移除它们。
soft – 软引用:基于垃圾回收器状态和软引用规则移除对象。
weak – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 lru。

flushinterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readonly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示:二级缓存是事务性的。这意味着,当 sqlsession 完成并提交时,或是完成并回滚,但没有执行 flushcache=true 的 insert/delete/update 语句时,缓存会获得更新。

 

框架前几部分都是属性文件解析,构造工厂,生产产品做真正的事

记一次使用mybatis使用出现的排序问题: ${}和 #{}的区别

由于开始时排序字段,特别是分页查询时,传的参数有第几页,每页几条记录,排序字段,是动态设置的,有时从前端传来,按某些个字段排序

/**
     * 学生查询考试任务列表
     * @param user
     * @param pagesize
     * @param pagenumber
     * @param status,试卷状态(0-n, 1. 未发布,可编辑修改和删除, 2. 已发布,可查看和停用, 3. 已结束,可审阅, 默认为0代表所有状态)
     * @param name, 试卷名字模糊查询
     * @param sortorder, 排序字段
     * @return
     */
    @suppresswarnings({ "rawtypes", "unchecked" })
    @requestmapping(value = "/list", method = requestmethod.get, produces = "application/json; charset=utf-8")
    @loginrequired
    @crossorigin
    public jsonobject list(@currentuser user user,
            @requestparam(value = "pagesize", required = false, defaultvalue = "10")integer pagesize, 
            @requestparam(value = "pagenumber", required = false, defaultvalue = "1")integer pagenumber, 
            integer status, 
            @requestparam(value = "name", required = false, defaultvalue = "") string name,
            @requestparam(value = "sortorder", required = false, defaultvalue = " cp.id desc ") string sortorder)

就是这个sortorder 

Hibernate入门这一篇就够了Hibernate入门这一篇就够了

 

大致就这样了,每个人都可以接着扩展开发。框架性迭代总是这样,xml到json,sql写在xml文件里到如今通过注解实现(spring也是如此,早期都是xml大量配置,如今也转换到注解配置),框架越来越包装了,愿每个码农都把基础学好,再学框架,不要一上来就mybatis,据说新码农都没写过原生servlet,更别提开发了。。。

总结:

自己早年封装的jdbc组件,简单易用,适合小规模化开发,主要当时技术受限,大量数据库连接没处理好,没有池化和缓存策略,且对码农的sql功底很强,其实面向sql编程

hibernate,个人觉得一个很不错得到orm框架,脱离了部分sql,只是没推广好

mybatis ,从架构上来说和hibernate雷同,生态圈建立的好,也有大厂光环。

汇总一句话:再好的orm,也脱离不了最最最基本的jdbc,劝扎进框架圈里的人,务必打好基础,框架各有千秋,没有最好,只要合适!!!