JDBC<br />1、 什么是JDBC<br />就是使用java语言来连接数据库。<br />2、 连接数据库的步骤<br />(1)导入jar包 :驱动包<br />(2)加载驱动类 :Class.forName(“类名”); (类名背下来)<br />(3)给出url,username,password (url背下来)<br />(4)使用DriverManager类来得到Connection对象<br /> 代码测试:TestJdbc2项目 Test.Demo01<br /> <br />3、 对数据库进行增、删、改操作<br />步骤:<br />1. 通过Connection对象创建Statement<br />Statement是语句发送器,功能是向数据库发送sql语句。<br />2. 调用它的int excuteUpdate(String sql); 它可以发生DML,DDL。<br />3. 关闭资源。(倒关。后创建的先关)<br />代码测试:TestJdbc2项目 Test.Demo02<br /> <br />4、 对数据库进行查询操作<br />代码测试:TestJdbc2项目 Test.Demo03<br />步骤:<br />1. 通过Connection对象创建Statement。<br />2. 调用它的excuteQuery(String querySql); 返回ResultSet。<br />3. 对查询返回的‘表格’(ResultSet)进行解析。<br />![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011995411-2f097d10-28c9-4376-bfe7-b2b74a88e7e9.png#height=140&width=415)<br />a).把行光标移到第一行,可以调用next()方法<br />b).getXxx()方法读取数据。<br /> getXxx(“列名”);<br />getXxx(列序号) 列序号从1开始。<br />4. 关闭资源。(倒关。后创建的先关)<br /> <br /> <br /> <br />5、 代码规范化<br />![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011995844-8a2e8f37-c161-4c09-b988-eb60f0d603a0.png#height=225&width=415)<br />6、 DriverManager<br />(1) Class.forName(“com.mysql.jdbc.Driver”);<br />抛出ClassNotFoundException异常。<br />原因:<br /> 1.没有导入mysql的jar包<br /> 2.将类名打错。检查类名是不是”com.mysql.jdbc.Driver”<br />(2) DriverManager.getConnection(url,username,password);<br />抛出SQLException异常。<br />原因:<br /> 三个参数是否有错。(url,username,password)<br />7、 Connection<br />(1) 获取Statement :createStatement();<br />(2) 获取Statement :createStatement(int,int);<br />这两个参数是用来确定创建的Statement能生成什么样的结果集。<br />8、 Statement<br />(1) int excuteQuery(String sql);<br />执行DML,DDL语句(实现对表和数据库的增删改操作)。返回值表示影响的行数。<br />(2) boolean excuteUpdate(String querySql);<br />执行查询操作,返回RseultSet,即结果集。<br />(3) boolean excute(); (了解)<br />1. 可以执行所有sql语句。返回的是boolean类型,表示Sql语句是否有结果集。<br />2. 执行更新语句时,需要调用int getUpdateCount()获取insert、update、delete语句所影响的行数。<br />3. 执行查询语句时,需要调用ResultSet getResultSet()来获取select语句的查询结果<br />9、 ResultSet之滚动结果集(了解)<br />结果集的特性:<br />概述:当使用createStatement时,已经确定了Statement生成的结果集是什么特性。<br /> 种类:<br />(1) 是否可滚动<br />(2) 是否敏感 : 数据库改变影响结果集<br />(3) 是否可更新 :结果集改变反向影响数据库<br /> <br /> createStatement():生成的结果集:可滚动,不敏感,不可更新 <br /> createStatement(int,int)<br /> 第一个参数:<br /> ResultSet.TYPE_FORWARD_ONLY :不滚动结果集<br /> ResultSet.TYPE_SCROLL_INSENSITIVE :滚动不敏感结果集<br />ResultSet.TYPE_ SCROLL_SENSITIVE :滚动敏感结果集<br /> 第二个参数:<br />ResultSet.CONCUR_READ_ONLY : 结果集是只读的,不能通过修改结果集而反向影响数据库<br />ResultSet.CONCUR_UPDATABLE : 结果集是可更新的,对结果集的更新可以反向影响数据库<br />![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011996293-97d2fc64-afa3-4183-9988-22c18b22d51f.png#height=210&width=415)<br /> ![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011996510-2733cbd1-19eb-4c08-bf0b-073ec612235b.png#height=18&width=203)<br /> <br />获取结果集的元数据(对列的操作):<br /> 得到元数据:rs.getMetaDate(),返回值为ResultSetMetaData<br /> 得到结果集列数:int getColumnCount()<br /> 得到指定列的列名:String getColumnName(int colIndex)<br /> <br />10、PreparedStatement<br />(1) 概述:是Statement的子接口<br />(2) 强大之处:<br />1. 防SQL攻击<br />2. 提高代码的可读性、可维护性<br />3. 提高效率<br />(3) SQL攻击<br />代码测试:TestJdbc2项目 Test.Demo04<br />(4) 用法:<br />代码测试:TestJdbc2项目 Test.Demo05<br />1. 如何得到PreparedStatement对象:<br />a).给出SQL模板<br /> 所有的参数的值都用?替代<br />b).调用Connection的<br />PreparedStatement PreparedStatement(String sql模板);<br />2. 为参数赋值<br /> 调用PreparedStatement的setXxx()系列方法<br /> setXxx(?的序号,参数值) 序号从1开始<br />3. 调用 excuteUpdate()或excuteQuery()方法<br />这些方法都没有参数。<br />(5) 预处理的原理<br />1、服务器的工作:<br />1. 校验SQL语句的语法<br />2. 编译:一个与函数相似的东西<br />3. 执行:调用函数<br />2、PrepareStatement:<br />1. 前提:连接的数据库必须支持预处理!几乎都支 持!<br />2. 每个PrepareStatement都与一个sql模板绑定在一起,先把sql模板给数据库,数据库先进行校验,再进行编译。执行时只是把参数传递过去而已。<br />3. 第二次执行,就不用再次校验语法,也不用再次编译,而是直接执行语句!<br /> <br />3、预处理的功能默认是关闭的<br /> url后设置参数,用于打开预处理。<br /> useServerPrepStmts=true<br /> cachePrepStmts=true<br /> <br />10、写出JdbcUtils的工具类(1.0版本)<br /> (1)加载配置文件(配置文件放在src下)<br /> InpuStream is=JdbcUtils.class.getClassLoader().<br />getResourceAsStream(“dbconfig.properties”);<br /> Properties pro=new Properties();<br /> pro.getProperty(“Property文件中的键名 ”)<br /> (2)加载驱动类<br />(3)获取Connection对象。<br />11、面向接口编程<br /> 代码测试:WebProject3项目<br /> 1.修改项目WebProject3。<br /> a).往Dao包添加DaoFactory<br />b).添加接口UserDao<br /> c).修改UserdDao为UserDaoImpl,并实现UserDao接口<br /> d).修改UserService中对UserDao的实例化:<br />private UserDao userDao=DaoFactory.getUserDao();<br /> <br /> 2.项目WebProject3的数据库改为mysql<br /> a).添加JdbcUserDao类,实现UserDao接口 <br /> b).添加Jdbcdao.properties,dbconfig.propertis,jar包<br /> <br />12、时间类型<br />(1)数据库类型与java类型的对应关系<br /> DATEjava.sql.Date<br /> TIMEjava.sql.Time<br /> TIMESTAMPjava.sql.Timestamp<br /> (2)注意事项<br /> 1. 领域对象(domain)中的所有属性不能出现java.sql包下的东西<br /> 2. ResultSet的getDate()返回的是java.sql.Date<br /> 3. PreparedStatement的 setDate(int,java.sql.Date)<br /> <br /> (3)时间类型的转换<br /> 代码测试:TestJdbc2项目 Test.Demo06<br /> 1.java.util.Datejava.sql.Date、Time、Timestamp<br /> 把util包下的Date转换成毫秒值<br /> 使用毫秒值创建java.sql.Date<br /> 2. java.sql.Date、Time、Timestampjava.util.Date<br /> 这里不需要处理,直接将java.sql.Date、Time、Timestamp<br />赋值给java.util.Date即可。因为java.util.Date是java.sql.Date、Time、Timestamp的父类。<br />13、大数据<br /> (1)概述:大的字节数据,大的字符数据<br /> (2)获取Blod对象:<br /> Bold是接口,实现类是SerialBolb。<br />1. 有一个文件<br />2. 先把文件转化成byte[]。<br />3. 再使用new SerialBlob(byte[] b);<br /> <br />(3)将Blob变成硬盘上的文件<br /> 1.通过Blob得到输入流对象<br /> 2.自己创建输出流对象<br /> 3.把输入流的数据写到输出流中<br /> <br />(4)MySql默认是不允许传输大数据包的,要去配置文件中设置。<br /> max_allowed_packet=多少M<br />(5)案例:把Mp3保存到数据库中去。<br /> 方法:<br />setBlob(int,Blob) <br />setBlob(int,InputStream)<br /> 代码实现:TestJdbc2项目 Test.Demo07<br />(6)案例2:从数据库中读取Mp3<br /> 方法:<br />Bolb的getBinaryStream(); 返回InputStream<br /> 代码实现:TestJdbc2项目 Test.Demo07<br /> <br />14、批处理<br /> (1)概述:<br />1. 一批批的处理Sql语句。<br /> 2. 只针对增、删、改。<br /> 3. 执行了“批”后,“批”中的所有sql语句会被清空<br /> (2)方法<br /> 1. void addBatch(String sql) : 添加一条语句到“批”中<br />2. int[] excuteBatch() : 执行批中所有语句,返回值表示每天语句所影响的行数据<br /> 3. void clearBatch() : 清空“批”中所有语句<br /> 4. void addBatch() : 将语句添加到批中 <br /> 代码测试:TestJdbc2项目 Test.Demo08<br /> <br />15、事务<br /> (1)事务的四大特性<br />原子性:事务中所有操作是不可再分割的。所有操作要么全部失败,要么全部成功<br />一致性:事务执行后,数据库状态与其它业务规则保持一致<br />隔离性:在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会互相干扰<br />持久性:一旦事务提交成功,事务中所有的数据操作都被必须被持久化到数据库中,即使提交事务后,数据库马上奔溃,在数据库重启时,也必须能保证通过某种机制恢复数据。<br /> <br /> (2)MySql中的事务<br />概述:默认情况下,MySql每执行一条语句,都是一个单独的事务。如果需要在一个事务中包含多条语句,需要开启和关闭事务。<br />开启事务:start transaction<br />结束事务:commit或rollback<br />commit : 成功结束事务,事务中的语句会影响数据库。 <br /> rollback : 失败结束事务,事务中的语句不会影响数据库。<br /> <br />(3)JDBC中的事务<br /> 代码测试:TestJdbc项目 Test.Demo09<br /> 概述: 在jdbc中处理事务,都是通过Connection完成的。<br /> 同一事务的所有操作,都在使用同一个Connection对象<br /> 方法:<br />1、 setAutoCommit(boolean) : 设置是否为自动提交事务,如果是 true(默认为true)表示自动提交,也就是每条sql语句都是一个单独的事务。如果是false,那么相当于开启了事务。<br /> <br />2、 commit() : 成功提交并结束事务<br />3、 rollback() : 回滚结束事务,失败提交<br /> <br />格式:<br /> try{<br /> conn. setAutoCommit(false);<br /> ………<br /> conn.commit();<br />}catch(Exception e){<br /> try{<br /> conn.rollback();<br /> conn.close();<br />}catch(SQLException e1){<br />e1.printStackTrace();<br />}<br /> <br /> (4)事务的隔离级别<br /> 1、并发事务问题<br /> 脏读: 读到另一个事务未提交的更新数据(不允许出现)<br />不可重复读:对同一记录的两次读取不一致,因为另一事务对该记录做了修改<br />幻读(虚读): 对同一张表的两次查询不一致,因为另一事务插入了一条记录(MySql无法测试到 )<br /> 2、4大隔离级别<br />SERIALIZABLE串行化(可以解决三种读问题):<br />1. 不会出现任何并发问题。因为它对同一数据的访问是串行的。<br />2. 性能最差。<br />REPEATABLE READ可重复读(MySql默认):<br />1. 防止脏读和不可重复读<br />2. 性能比SERIALIZABLE好<br /> READ COMMITTED读已提交数据(Oracle默认):<br />1. 只能处理脏读<br />2. 性能比REPEATABLE READ好<br /> READ UNCOMMITTED(读未提交数据):<br />1. 可能出现任何事物并发问题<br />2. 性能最好<br /> <br /> 3、查看数据库的隔离级别<br /> Select @@tx_isolation<br /> 4、数据库设置事务的隔离级别<br /> Set transaction isolationlevel [4选1]<br /> 5、JDBC中设置事务的隔离界别 <br />conn.setTransactionIsolation(Connection.xxx);<br /> <br />16、数据库连接池<br /> (1)池参数(Connection个数)<br /> 初始大小、最小空闲连接数、增量、最大空闲连接数、<br /> 最大连接数、最大等待时间<br /> (2)概述:<br /> 1、连接池也是使用四大连接参数来完成创建连接对象!<br /> 2、连接池必须实现javax.sql.DataSource接口(已经被人完成)<br /> 3、池参数都有默认值<br /> <br /> (3)DBCP:<br /> 特点:<br />返回的Connection对象的close方法与众不同。调用close()方法不是关闭,而是把连接归还给池!<br /> 创建步骤:<br /> 代码测试:TestJdbc2项目 Test.TestDBCP<br /> 1、导入包:commons-dbcp.jar commons-pool.jar<br /> 2、创建连接池对象<br /> BasicDataSource类<br />3、配置四大参数<br />setDriverClassName(“com.mysql.jdbc.Driver”);<br />setUrl(“jdbc:mysql://localhost:3306/数据库名”);<br />setUsername(“”);<br />setPassword(“”)<br />4、配置池参数<br />最大连接数:setMaxActive()<br />最小空闲连接:setMinIdle()<br />最大空闲连接:setMinIdle ()<br />最大等待时间:setMaxWait() 单位:毫秒<br />5、得到连接对象<br />1.得到的连接对象是mysql提供的Connection<br />2.连接池将连接对象进行了装饰,只对close方法进行了增强。(装饰者模式)<br />3.装饰之后的close()方法,用来将当前连接归还给池<br /> <br /> (4)C3P0(开发优先使用)<br /> 代码测试:TestJdbc2项目 Test.TestC3P0<br /> 一、创建步骤:<br /> 1、导入包:c3p0-0.9.5.2.jar mchange-commons.jar<br /> 2、创建连接池对象<br /> ComboPooledDataSource类<br />3、配置四大参数<br />setDriverClass (“com.mysql.jdbc.Driver”);<br />setJdbcUrl(“jdbc:mysql://localhost:3306/数据库名”);<br />setUser (“”);<br />setPassword(“”)<br />4、配置池参数<br />5、得到连接对象<br /> <br /> 二、对配置文件的使用:<br />1、在创建连接池对象时,这个对象就会自动加载配置文件。不用我们指定路径。<br /> 2、文件的名称必须是:c3p0-config<br /> 3、文件的位置必须是:src下<br /> <br /> 4、默认配置格式:<br /><c3p0-config><br /> <default-config><br /> <property name=“参数名”>参数值</property><br /> </default-config> <br /> </c3p0-config><br /> new ComboPooledDataSource();<br /> 无需设置四大参数,直接getConnection();<br /> <br /> 5、命名配置格式:<br /><c3p0-config><br /> <named-config name=”命名1”><br /> <property name=“参数名”>参数值</property><br /> </named-config> <br /> </c3p0-config><br /> new ComboPooledDataSource(“命名1”);<br /> 无需设置四大参数,直接getConnection();<br /> <br /> 6、优先级<br />连接池对象会先加载配置文件,但是又打代码来设置4大参数,后者会将前者覆盖。<br /> <br /> (5)Tomcat配置数据库连接池<br /> 1、JNDI<br /> 概述:java命名和目录接口<br />作用:在服务器上配置资源,然后通过统一的方式来获取配置的资源。<br /> <br />2、 我们这里要配置的资源就是连接池,这样项目就可以通过统一的方式来获取连接池对象<br /> <br />3、配置资源([数据库连接池配置的几种方法](https://blog.csdn.net/yetaodiao/article/details/12836993))<br /> 一、局部:<br />在项目的META-INF目录下创建context.xml文件,然后写入下图的配置。<br /> 二、全局:<br /> 修改conf/server.xml和context.xml。<br /> 往tomcat/lib下加入jar包<br /> 往项目下的web.xml添加<resource-ref> <br />name : 指定资源的名称,这个名称可以任意取。 Factory : 用来创建资源的工厂,值基本固定的。<br /> Type : 资源的类型。这里就是连接池类型。<br /> ![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011996908-bea548da-0f41-42d9-b599-716b20ea6867.png#height=232&width=371) <br />4、获取资源的代码<br /> 代码测试:TomcatPool项目 Test.AServlet<br />![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011997366-10bb5cdc-d5f7-43ef-a4c0-635fc41a845d.png#height=191&width=415) <br />17、对象增强的手段<br /> (1)继承<br /> 缺点:<br />1. 增强的内容是死的,不能动<br />2. 被增强的对象也是死的!<br /> ![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011997617-94d78d1a-0c0e-4ace-b07a-151da009ce2d.png#height=222&width=209)<br /> (2)装饰者模式<br /> 概述:<br /> 1、被增强的对象可以是任意的<br /> 2、增强的内容是固定的。<br /> 使用:<br /> 不知道被增强对象的具体类型时,可以使用装饰者模式 <br /> (是你还有你 ,一切拜托你!)<br /> 不变的地方继承过来,增强的内容则进行重写!<br /> ![](https://cdn.nlark.com/yuque/0/2020/png/471109/1608011997932-f4880c8c-f015-4787-b49f-5483ab0b2e07.png#height=173&width=296)<br /> (3)动态代理(AOP)<br /> 优点:<br />1、 被增强的对象可以切换<br />2、 增强的内容也可以切换<br /> <br />18、ThreadLocal<br /> (1)内部是个Map。key:线程。 Value : 任何值<br /> (2)方法:<br /> Set() remove() get()<br /> <br /> (3)能够产生多线程并发访问同一对象的是: 成员变量<br />代码测试: TestJdbc2项目 Test.TestThreadLocal<br /> <br />19、dbutils<br /> (1)作用:简化jdbc的代码<br /> (2)原理:<br /> 代码测试:TestJdbc2项目 dbutils.QR dbutils.TestQR dbutils.Stu<br /> (3)导包:commons-dbutils.jar包<br /> (4)使用步骤:<br /> 1、创建QueryRunner,需要提供数据库连接池对象(DataSource)<br /> 目的:提供了DataSource,方法就可以不需要传Connection<br /> 2、准备sql语句模板<br /> 3、将sql模板的参数放入Object数组中<br /> 4、执行相应的update、query方法<br /> <br /> (5)QueryRunner<br /> int update(String sql,Object…params)<br /> 执行增、删、改语句。不能保证两次调用都是同一个Connection对象。<br /> int update(Connection con,String sql,Object…params)<br /> 执行增、删、改语句。需要调用者提供Connection。可以保证两次调用都是同一个Connection对象,支持事务。 <br /> <br />T query(String sql,ResultSetHandler<T> rsh,Object…params)<br /> 1、执行查询语句<br />2、它会先得到ResultSet,然后再调用rsh的handle()把rs转换成需要的类型<br /> <br /> T query(Connection con,String sql,ResultSetHandler rsh,Object…params)<br />需要调用者提供Connection。可以保证两次调用都是同一个Connection对象,支持事务。<br /> <br /> T query(String sql,ResultSetHandler rsh)<br /> 无条件查询<br /> T query(Connection con,String sql,ResultSetHandler rsh)<br /> 无条件查询。需要调用者提供Connection对象。支持事务。<br /> <br />案例1:向数据库插入记录<br />代码实现:TestJdbc2项目 dbutils.Demo01<br /> <br />案例2:查询数据库中的某一条记录<br />代码实现:TestJdbc2项目 dbutils.Demo02<br /> <br /> (5)结果集处理器(ResultSetHandler的实现类)<br />1、 BeanHandler(单行)<br />概述:构造器需要一个Class类型的参数,用来把一行结果转换成指定类型的javaBean对象<br />使用:new BeanHandler<T>(T.class);<br />返回值类型:T<br />注意:T的javabean属性必须和数据库的列名相同。 <br />2、 BeanListHandler(多行)<br />概述:构造器需要一个Class类型的参数,用来把一行结果集转换一个javabean,那么多行就是转换成List对象,一堆javabean<br />使用:new BeanListHandler<T>(T.class);<br />返回值类型: List<T><br />代码测试:TestJdbc2项目 dbutils.Demo03<br /> <br />3、 MapHandler(单行)<br />概述:把一行结果集转换成一个Map对象<br /> 一行记录:<br /> number name age gender<br /> 1001 zs 99 man<br /> 一个Map:<br /> {number:1001 name:zs age:99 gender:man}<br />使用:new MapHandler();<br />返回值类型:Map<br />代码测试:TestJdbc2项目 dbutils.Demo04<br /> <br />4、 MapListHandler(多行)<br />概述: 把一行记录转换成一个Map对象,多行就是多个Map,即List<Map>!<br />使用:new MapListHandler();<br />返回值类型:List<Map<String,Object>><br />代码测试:TestJdbc2项目 dbutils.Demo05<br /> <br />5、 ScalarHandler(单行单列)<br />概述:通常用于select count(*) from stu语句。结果集是单行单列的。它返回一个Objet<br />使用:new ScalarHandler();<br />返回值类型:Object<br />代码测试:TestJdbc2项目 dbutils.Demo06<br /> <br />20、Service层下完成事务处理<br /> (1)业务逻辑在 : Service层<br /> (2)Connection对象 : Dao层<br /> 解决方法:将事务的开启,提交,回滚放在JdbcUtils中。<br /> <br />21、JdbcUtils(3.0版本)<br /> <br /> 代码实现1:<br /> 1、加入了事务功和关闭连接功能。<br />TestJdbc2项目 Utils.JdbcUtils03 Utils.AccountDao01 Utils.Demo01<br /> <br />代码实现2:<br />1、TxQueryRunner类继承QueryRunner,重写update、query中形参不带Connection的所有不过时方法<br />2、AccountDao02只调用TxQueryRunner中重写的任意方法。<br /> TestJdbc2项目 Utils.JdbcUtils03 Utils.TxQueryRunner <br />Utils.Demo02 Utils.AccountDao02<br /> 代码实现3:<br /> 1、添加:处理多线程并发访问问题。<br /> TestJdbc2项目 Utils.JdbcUtils03 Utils.Demo03