zoukankan      html  css  js  c++  java
  • java操作数据库的几种方式

    历史就是一面镜子

    回顾自己开发的历程,见证了时代变迁史记,下面我针对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、释放资源.

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


    对应的驱动包 


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

    示例代码如下:

    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("
    ");
                }
            } 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();
                }
            }
        }
    }
    View Code

    查询结果

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


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

    池化简单点说就是预先连接好一定数量的连接,等需要时随意从中选择一个进行操作。略了,当时主要用的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。

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

      //获取加载配置管理类
            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 教程 https://www.w3cschool.cn/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();
            }
        }
    }

    配置文件三个如下

     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>

    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>

     数据库配置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

    执行测试代码效果:

    ​ 

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

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

    一级缓存

    一级缓存 Mybatis的一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。 在同一个SqlSession中,执行相同的SQL查询时;第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中取。 当执行SQL时候两次查询中间发生了增删改的操作,则SQLSession的缓存会被清空。 每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。 Mybatis的内部缓存使用一个HashMap,

     public class PerpetualCache implements Cache {
    
      private final String id;
    
      //是个map对象
      private final Map<Object, Object> cache = new HashMap<>();
    
      public PerpetualCache(String id) {
        this.id = id;
      }
    
      @Override
      public String getId() {
        return id;
      }
    
      @Override
      public int getSize() {
        return cache.size();
      }
    
      @Override
      public void putObject(Object key, Object value) {
        cache.put(key, value);
      }
    }

    key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。 SqlSession执行insert、update、delete等操作commit后会清空该SQLSession缓存。

    几种情况:

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


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

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


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

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


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

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

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

     小结:一级缓存是SqlSession级别的缓存,Mybatis默认是开启一级缓存的,当调用SqlSession的修改、添加、删除、commit()、close()等方法时,就会清空一级缓存。

    二级缓存

    之所以称之为“二级缓存”,是相对于“一级缓存”而言的。既然有了一级缓存,那么为什么要提供二级缓存呢?我们知道,在一级缓存中,不同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"/>

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

    可用的清除策略有:

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

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

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

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

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

     小结: 二级缓存是mapper映射级别的缓存,默认是不开启的,多个SqlSession去操作同一个Mapper映射的sql语句是共用二级缓存的,二级缓存是跨SqlSession的。

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

    记一次使用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 


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

    总结:

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

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

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

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

    参考:

    0. Develop Java applications with Oracle Database   https://www.oracle.com/database/technologies/appdev/jdbc.html

    1.  程序员的SQL金典https://github.com/dongguangming/java/blob/master/%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84SQL%E9%87%91%E5%85%B8.pdf

    2.  JNDI数据库连接池配置 https://www.iteye.com/blog/xiaoliang330-978823

    3   Hibernate入门这一篇就够了  

    4. Java Mybatis框架入门教程  http://c.biancheng.net/mybatis/

    5. JDBC Example – MySQL, Oracle https://www.journaldev.com/2471/jdbc-example-mysql-oracle

  • 相关阅读:
    【Vegas原创】mysql更改用户密码之无敌方法
    【Vegas原创】Xcopy屡试不爽
    【Vegas原创】ctrl shift无法切换输入法的解决方法
    【Vegas原创】将SQLServer表、视图、存储过程的所有者批量改为dbo的处理方法
    【Vegas原创】SQL Server2005应急备机切换步骤 生产机正常
    【Vegas原创】SQLServer 2000 企业管理器展开数据库列表错误的解决方法
    【Vegas原创】win7下打开telnet服务
    【Vegas原创】Windows 2003下CACTI的安装及配置
    【Vegas原创】SecureCRT个性化设置
    Mathematica实现微分算子功能
  • 原文地址:https://www.cnblogs.com/dongguangming/p/12742533.html
Copyright © 2011-2022 走看看