1.报错:Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:59) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:83)
解决办法:是时区的错误,因此需要设置为你当前系统时区即可
"jdbc:mysql://localhost:3306/eesy_mybatis"?serverTimezone=GMT%2B8
Mybatis中用到的设计模式
(1)构建者模式,(2)工厂模式
还用到了反射机制
手动创建:
1.创建代理对象
2.实现查询所有
3.事务回滚之后,自增的id仍然会自增
报错:Caused by: java.lang.NullPointerException at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(DefaultSqlSessionFactory.java:95) ... 32 more
解决:
<!--配置环境-->
<environments default="mysql">
<!--配置mysql的环境-->
<environment id="mysql">
报错:
Caused by: java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mysql.jc.jdbc.Driver
at org.apache.ibatis.datasource.unpooled.UnpooledDataSource.initializeDriver(UnpooledDataSource.java:241)
解决:
<property name="driver" value="com.mysql.jdbc.Driver"></property>
问题:
提交事务后:
OGNL表达式:
他是通过对象的取值方法来获取数据,在写法上把get给省略了。
比如:我们获取用户的名称
类中的写法:user.getUsername()
OGNL表达式写法:user.username
mybatis中为什么能直接写username,而不用user呢
因为在parameterType中已经提供了属性所属的类,所以此时不用写对象名。
传递poji包装对象
需求:根据用户名查询用户信息,查询条件放到QueryVO的user属性中。
把实体类又包装了一层。。
属性不一致问题:
Mysql数据库会把实体类中的属性userName和数据库表中的列明username为同一个!所以在设置值的时候可以封装进去。但是在Linux操作系统中是严格区分大小写的!
如果遇到数据库和实体类中的字段名不一致的现象解决办法
(1)更改sql语句给字段起别名as uid等
(2)Mybatis自己解决利用配置文件进行配置再
中进行配置主 键和个字段名称property
实体类属性名, column
数据库字段名。
然后更改resultType属性换成resultMap属性,如下:
Dao实现CRUD:
Dao实现类执行过程分析
(1)findAll查询所有方法session.selectList()底层是怎样调用的。。
SqlSession -->defaultSqlSession–>seleList()方法执行–>executor.query(,)–>CachingExecutor.class–>query(,)方法–>else return delegate.query()方法
然后–>SimpleExecutor.class–>类中的只有doQuery()方法–>SimpleE的父类BaseExecutor–>BaseExecutor类中的query方法–>这个方法中调用了else this.queryFromDatabase()方法–>点进去(执行了)找到了doQuery方法–>再点进去发现abstract List doQuery是抽象方法。
所以这个抽象方法被SimpleExecutor给实现了,所以最终执行的是SimpleExecutor类中的doQuery()方法。。
SimpleExecutor.doQuery()方法–>handler–>StatementHandler—>public interface StatementHandler–>打开图标,找到打开RountingStatementHandler–>找到query方法 -->返回了delegate.query()方法
然后找到StatementHandler
接口的实现类PreparedStatementHandler
–>中找到PreparedStatement
是一个代理对象,对象就是我们想要的JDBC中的预处理对象。其中的execute方法既能执行查询也能进行增删改。return this.resultSetHandler.handleResultSets(ps);
对结果集进行封装。–>resultSetHandler
点进去是一个抽象类–>点击其中的ResultSetHandler属性,–>ResultSetHandler
是一个接口–>然后去找他的diagram实现类–>就一个DefaultResultSetHandler
–>上面它调了handleResultSets(ps)
方法,在其中找到这个方法它调用的handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
这个方法不停的取值和赋值。
(2)分析插入删除更新方法
在实现类中直接进入insert()方法SqlSession是一个接口,直接diagrams找到它的实现类–>defaultSqlSession–>找到他的insert()方法,它最终调用的是update()方法,如下
再看delect方法,最终也是执行了update()方法
所以说三个操作最终都会执行update()方法–>然后执行里面的excetor()方法,去调用它的update()方法
接下来–>找到excutor这个接口–>但后在diagrams找到它的实现类CachingExcutor这个类–>找到其中的update()方法,如下:
它又调用了delegate的update()方法,然后再去找executor的实现类BaseExecutor—>中的update()方法,如下:
它又调用的doUpdate()方法,点进去这个方法在里面是一个抽象的方法,–>所以要找到BaseExecutor的子类SimpleExecutor–>再找到其中的doUpdate()方法,如下:
它又调用了handler的update()方法,找到这个对象所属的类StatementHandler是一个接口–>diagrams找到他的实现类RountingStatementHandler–>找到其中的update()方法
它又调用了delegate的update()方法,debug调试查看到它的PreparedStatementHanldler类的实例,—>继续找到StanementHandler接口的是实现类PreparedStatementHanldler类中的update()方法,如下
PreparedStatement对象和execute()都已经出来了,我们传统的JDBC操作出来了。
三个方法无论是谁走到最后都是走到preparedStatement。
代理Dao执行过程分析
创建代理对象getMapper()
方法–>点进去SqlSession
是一个接口diagram找到它的实现类defaultSqlSession–>找到getMapper()
方法
后–>他又调用了configuration
的.getMapper()
方法–>点击configuration
就直接进入了Configuration.class
这个类—>找到getMapper
方法
进入mapperRegistry
,MapperRegistry.class
这个类–>找到getMapper()
方法
然后再进入其newInstance(sqlSession)
方法
其中的newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy)
newProxyInstance(类加载器,实现类接口,如何代理)就是动态代理–>点击里面的mapperProxy
也就是MapperProxy
类,–>发现这个类实现了InvocationHandler, Serializabl
这个接口,有invoke方法
应该是有一个返回execute方法,但是我没找到。。
我找到了一个他的内部类private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker
里面的invoke()方法,返回 return this.mapperMethod.execute(sqlSession, args);
–>点击execute()
方法,进入MapperMethod.class
类中找到execute()方法,在这个方法中找到executeForMany点进去就会看到最终要执行的selectList()方法,到此结束
properties标签的使用及细节:
<properties>
<property name="password" value="123456"></property>
</properties>
<dataSource type="POOLED">
<!--去引用上面的property-->
<property name="password" value="${password}"></property>
</dataSource>
可以把他放在一个外部文件里面
typeAliases标签和package
(1)typeAliases
<typeAliases>
<!--typeAlias用于配置别名,type属性指定的是实体类的权限定类名,而alias属性指定别名,当指定了别名,就不区分大小写-->
<!--<typeAlias type="com.selan.domain.User" alias="user"></typeAlias>-->
<!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="com.selan.domain"></package>
</typeAliases>
配置完之后可以把IUserDao中的parameterType="com.selan.domain.User
改为如下
起的别名,不区分大小写。
<update id="updateUser" parameterType="user">
UPDATE USER SET username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
</update>
(2)package
也是在typeAliases标签中写
<!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且**类 名 就 是 别 名**,不再区分大小写-->
<package name="com.selan.domain"></package>
在mappers标签中配置
<mappers>
<!--<mapper resource="com/selan/dao/IUserDao.xml"></mapper>-->
<!--package用于指定dao接口所在的包,当指定完成之后,就不需要再写resource,mapper,class了-->
<package name="com.selan.dao"></package>
</mappers>
1.mybatis中的连接池以及事务控制: 原理部分了解,应用部分会用
mybatis中的连接池使用及分析
mybatis事务控制分析
2.mybatis基于XML配置的动态SQL语句使用
mappers配置文件中的几个标签
3.mybatis中的多表操作
一对多
一对一
多对多
=========================================
1.连接池:
用处很广泛
因为它可以减少我们获取连接所消耗的时间 。
所谓连接池其实就是一个存储连接的容器,容器就是一个集合对象。该集合必须是线程安全的,不能两个线程拿到同一连接。该集合还必须实现队列的特性:先进先出。
2.mybatis中的连接池
mybatis连接池提供了3中方式的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池的方式。
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
type属性的取值:
UNPOOLED
每次创建一个新的连接,最后closeing掉
UNPOOLED原理分析:
ctrl+shift+t查找类UnpooledDataSource和PooledDataSource这两个类都实现了DataSource。
在UnpooledDataSource类中找到getConnection()
方法–>z在方法中它调用了doGetConnection(username, password);
点进去 -->方法中设置了用户名和密码,又再次调用了doGetConnectionreturn this.doGetConnection(props);
–>继续点进去看到如下
注册驱动,获取连接,返回连接。
POOLED原理分析
PooledDataSource类中的getConnection
方法调用了popConnection()
方法–>点进去看到如下:
最后对oldestActiveConnection进行一下设置:保证这个oldestActiveConnection是一个全新可以使用的。
state
的idleConnections
是空闲连接的意思if (!this.state.idleConnections.isEmpty())
–>点进去idleConnection
发现它是一个集合protected final List
mybatis中的事务
什么是事务
事务的四大特性ACID
不考虑隔离性会产生的三个问题
解决办法:四种隔离级别
mybayis中自动提交事务的设置:
它是通过sqlSession对象和commit方法和rollback方法实现事务的提交和回滚。
分析流程源码:
sqlsession.commit();
点击进入commit()方法—
就这样一路往下找----->找到BaseExecutor类中的commit方法,在方法中看到 this.transaction.commit();-
-点进入commit是一个接口Transaction,找到它的实现类JdbcTransaction其中的方法如下
是一个connection对象调用的,而这个connection对象,是java.sql的connection对象,也就是传统的原始的JDBC操作。
点进去测试类的openSession()方法进入人SqlSessionFactory类里面有一个方法 SqlSession openSession(boolean var1);
自动提交。
所以在创建的时候把openSession方法改成一个true值,如下:
sqlsession = factory.openSession(true);
就可以不同写提交操作,依然能够实现增删改:
// sqlsession.commit();
因为在设置提交的时候,我们把手动提交改成了自动提交true。控制台信息如下
### The error may exist in com/selan/dao/IUserDao.xml
### The error may involve com.selan.dao.IUserDao.findUserByCondition-Inline
### The error occurred while setting parameters
### SQL: select * from user WHERE 1=1**;** AND username = ?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AND username = '老王'' at line 3
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
解决:
在抽取出代码片段sql语句时,尽量不要写分号,因为在拼接的时候后面还有语句。
Mybati中的多表表查询(重要)
表之间的关系分为: 一对多,多对一,一对一,多对多,
举例:一个用户可以下国歌订单;多个订单可以同属于一个用户;
用户和订单是一对多,订单和用户是多对一。
特例:
如果拿出每一个订单,它都只属于一个用户
所以mybatis就把多对一看成一对一。
mybais多表查询:
示例:用户和账户。一个用户可以有多个账户,一个账户只属于一个用户(多个账户也可以属于一个用户)
步骤:
1.先建立两张表 用户表,账户表。让用户表和账户表之间具备一对多的关系;需要用外键在账户表中添加。
2.建立两个实体类,用户实体类和账户实体类,让用户和账户的实体类能体现出来一对多的关系
3,创建两个配置文件。用户配置文件和账户配置文件
4.实现配置:当我们查询用户时,可以同时得到用户下所包含的账户信息
;当我们查询账户时,可以同时得到账户的所属用户信息。
完成account一对一操作-通过写account子查询方式:
查询所有账户同时包含用户名和地址信息:
完成account一对一操作-建立实体类关系的方式:
完成user的一对多查询操作
因为我们要查询所有用户及其他的账户信息,所以所有的用户信息必须查出,因此我们不能再用inner join(user,account where…)内链接查询了.我们应该用左外连接查询(返回所有左表的信息),如下:
分析mybatis中多对多的的应用
示例:用户和角色。一个用户可以有多个角色,一个角色可以赋予多个用户。
步骤:
1.建立两张表,用户表和角色表,让用户表和角色表有多对多的关系,需要使用中间表,中间表中包含各自的主键,在中间表中时外键。
2.建立两个实体类,用户实体类和角色实体类,让用户和角色的实体类能体现出来多对多的关系,各自包含对方一个集合的引用。
3,创建两个配置文件。用户配置文件和角色配置文件
4.实现配置:当我们查询用户时,可以同时得到用户下所包含的角色信息
;当我们查询角色时,可以同时得到角色的所赋予的用户信息。
(1)查询角色获取角色下所有的用户信息。
所以目标就是查询所有角色,同时获取角色的所赋予的用户,通过查看中间价表就可以看出来。
起别名:
(2)查询用户时,可以同时得到用户下所包含的角色信息.
同理,只需要修改select 语句即可,
select u.*,r.id as rid,r.role_name,r.role_desc from user u
left outer join user_role ur on u.id=ur.uid
left outer join role r on r.id=u`r.rid`
得到是 :用户的全部信息和每个用户所具备的角色。
JNDI概念和接口:
是sun公司提供的一种标准的java命名系统接口
配置并启动tomcat服务器
在index.jsp中添加如下:进行测试
学习过程中遇到报错
Cause: javax.naming.NameNotFoundException: Name [jdbc/eesy_mybatis] is not bound in this Context. Unable to find [jdbc].
org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
Day04
Mybatis中的延迟加载:
问题:(1)在一对多中,一个用户有100个账户,在查询用户的时候,要不要把关联的账户查出来?
(2)在查询账户的时候,要不要把关联的用户查出来?
所以在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询
所以在查询账户时,账户的所属用户用户信息应该是随着账户查询时一起查出来的。
什么是延迟加载:在真正使用数据时才发起查询,不用的时候不查询。查需加载(懒加载)
什么是立即加载:不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:
一对多,多对多:通常情况下使用延迟加载,
一对一的延迟加载:
执行了三条语句:但并没用实现延迟功能
开始进行mybatis配置
在测试文件中把遍历进行注释:
再次运行:
一对多的延迟加载:
多对一,一对一:通常都是使用立即加载
Mybatis中的缓存(重点)
什么是缓存:存在于内存中的临时数据
为什么使用缓存:减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,神魔样的数据不能使用:
适用于缓存的:经常查询的数据并且不经常改变的,数据的正确与否对最终的结果影响不大。
不适用于缓存的:数据经常改变,数据的正确与否对最终的结果影响较大的(会产生很大的损失,商品的库存,银行的汇率,股市的排价)
Mybatis中的一级缓存和二级缓存
一级缓存:指的是Mybatis中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一快区域中,该区域的结构式一个Map,当我们再次查询同样的数据时,mybatis会先去SqlSession中去查询有没有,有的化直接拿出来;当SqlSession对象消失时,mybatis的一级缓存也就会随之消失。
为true
就查询了一次,说明第二次是从缓存中拿出来的。
当SqlSession对象消失时:
为false,获取了两次SqlSession对象,两个connection存在
还有一种方法可以关闭一级缓存
触发清空一级缓存的情况:
当调用SqlSession的修改、添加、删除、commit(),close()等方法时,。就会清空一级缓存。
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在update/select标签中配置)
二级缓存中存放的时对象,因为他创建了一个新的用户对象,所以两个对象不是同一个对象了,是false!
Mybatis中的注解开发
我们可以减少编写Mapper映射文件
环境搭建
单表CRUD操作(代理dao方式)
只要用到了这个
不管有没有删除IUserDao都会报错
Mybatis注解解决实体类属性名和数据库列明不匹配问题
ResultMap里面有一个字符串数组,所以说它可以引用多个
@ResultMap(value = “userMap”,“dad”,“dasd”)
多表查询操作
一对一的查询配置
报错小提示:
这种报错是因为配置文件的原因
一个账户只能对应一个用户
点进FethType看到:
对一的时候通常选择立即加载
对多的时候通常选择延迟加载
一对多的查询配置
一个用户可以对应多个账户:
配置了延迟加载,
最后,无论是一对多,还是多对一,在配置中只需要关注两个属性:如下:
缓存的配置
二级缓存:
这样执行一定会是干了两次:
开始进行配置:
注解缓存的配置
结果,发现只干了一次: