第三阶段课程安排:
天 | 课程内容 |
1 | mybatis基础 |
2 | mybatis高级 |
3 | AM:初步认识代理模式,了解mybatis源码 PM:权限模块dao层实现 |
4 | spring容器 |
5 | spring切面 |
6 | AM:spring类配置,切面 PM:定义事务管理器实现事务切面 |
7 | springmvc基础 |
8 | springmvc高级 |
9 | ssm框架整合 |
10 | 自习:完成至少两个业务模块 |
12 | redis缓存及使用redis重构代码 |
13 | 工具篇:maven项目管理工具 |
14 | AM:工具篇:maven聚合工程 |
15 | springboot基础 |
16 | springboot高级 |
17 | springboot高级 mybatisplus学习 |
18 | 工具篇:远程接口访问与定时任务,异步任务 |
19 | 工具篇:短信与邮件发送 |
20 | 工具篇:excel与word生成 |
21 | 工具篇:阿里云对象oss存储 |
22-30 | 项目篇:天翼电子经销存 |
1. Mybaits介绍
进入mybatis官网自己学习:
mybatis作用:(***)
是一个持久层的框架,是对jdbc的封装.提高代码开发效率.
是一个半自动orm框架.对象关系映射框架,把表与对象做一一映射,目的是以操作对象的方式来操作表,mybatis是半自动orm,因为mybatis需要开发人员自定义sql语句.
hibernate都是纯oop的orm框架.执行效率太低.
jdbc-->dbutils-->mybatis-->hibernate
2.50分钟入门mybatis
新建普通的java工程:
1.安装mybatis环境,mysql驱动包.
新建lib包,加入这两个jar包
2.准备数据库表,表对应的实体类
新建数据库
新建表
id先自增
创建表对应实体类
新建包
将创建时间和修改时间对应的数据类型改为String,对应的set,get修改。
3.创建表对应的xml映射文件(此文件可以放在dao包,可以放在resources目录下)
src目录下习惯只放java源代码。而xml文件不需要编译,故最好单独建立文件夹。
新建resources目录,存放资源文件。
在resources下新建mapper目录。idea下不支持创建xml文件,需要编辑一个文件模板。
从官网拷贝相关代码到⑤中即可。
<?xml version="1.0" encoding="UTF-8" ?> <!--xml声明--> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--模板文件声明,可规定当前xml文件可打哪些标签--> <mapper namespace=""> </mapper>
mapper目录下新建xml文件
起名sysuser-mapper
<!--namespace理解成类名,不同的映射文件namespace不能重复--> <mapper namespace="aa.bb"> <!-- Sysuser getById(Integer id){ DateSource ds; String sql = "select * from sys_user where id = ?"; Connection c = ds.getConnection(); PrepareStatement ps = c.prepareStatement(sql); ps.setInt(1,"10") ResultSet rs = ps.executeQuery() } 理解成daoImpl实现类中写的sql id对应方法名 只需要写sql语句 --> <select id="getById" parameterType="java.lang.Integer" resultType="com.javasm.entity.SysUser"> select * from sys_user where id =#{zzzzz} </select> </mapper>
4.创建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> </configuration>
resources目录下创建conf文件,起名mybatis
注:xml里不支持&符号
<configuration> <!--配置数据源. 支持多数据库环境,但目前该技术已淘汰。一二阶段的项目操作一个数据库的数据,实际中大型项目可能把不同业务的表放到不同 库里。分到不同库能减少访问压力,比如司机和乘客放在不同的库里。--> <environments default="704aDev"> <!--一个environment代表一个数据库的连接配置--> <environment id="704aDev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/704a"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--引入映射文件--> <mappers> <!--/开始表示根路径--> <mapper resource="/mappers/sysuser-mapper.xml"></mapper> </mappers> </configuration>
5.加载xml配置文件创建SqlSessionFactory
新建test测试源码目录
新建com.javasm.TestGetById
public class TestGetById { @Test public void test1_getUserById(){ //1.加载xml配置文件,读取为输入流 //类加载器,此对象作用是去根路径(src,resources都是根路径)加载文件 ClassLoader classLoader = TestGetById.class.getClassLoader(); InputStream in = classLoader.getResourceAsStream("mybatis.xml"); //获取文件输入流 //2.从xml中构建SqlSessionFactory,把该对象理解为DateSources连接池,即SqlSessionFactory里存放对某库的连接 //SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in,"704aDev"); //build的第二个参数即environment对应的id,不加该参数则为environments的默认id SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //3.从SqlSessionFactory中获取SqlSession,将SqlSession理解成Connection //session是会话对象,两端建立连接 SqlSession session = factory.openSession(); //4.curd增删改查 //相当于执行某个类下的某个方法 //selectOne方法只会查出一条记录。多条记录时,不能用该方法。 Object o = session.selectOne("aa.bb.getById", 1);//需要Integer类型的参数 //5.释放资源 session.close(); } }
用ClassLoader读取流,用户从服务器下载文件到本地(根据文件名找到该文件),常用该方法。
实际上,mybatis里有专门的工具类
InputStream in = Resources.getResourceAsStream("mybatis.xml");
底层就是用ClassLoader。
数据库表中增加一行记录
运行该代码,报错
如何看报错代码?
"C:\Program Files\Java\jdk-11.0.14\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:50557,suspend=y,server=n -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2020.1\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\idea\IntelliJ IDEA 2020.1.1\lib\idea_rt.jar;D:\idea\IntelliJ IDEA 2020.1.1\plugins\junit\lib\junit5-rt.jar;D:\idea\IntelliJ IDEA 2020.1.1\plugins\junit\lib\junit-rt.jar;F:\workspace\three\0920mybatis\out\test\0920mybatis;F:\workspace\three\0920mybatis\out\production\0920mybatis;F:\workspace\three\0920mybatis\lib\mybatis-3.4.1.jar;F:\workspace\three\0920mybatis\lib\mysql-connector-java-5.1.40-bin.jar;C:\Users\Administrator\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\Administrator\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 com.javasm.TestGetById,test1_getUserById Connected to the target VM, address: '127.0.0.1:50557', transport: 'socket' WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/F:/workspace/three/0920mybatis/lib/mybatis-3.4.1.jar) to method java.lang.Class.checkPackageAccess(java.lang.SecurityManager,java.lang.ClassLoader,boolean) WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release Disconnected from the target VM, address: '127.0.0.1:50557', transport: 'socket' org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession. ### The error may exist in /mappers/sysuser-mapper.xml ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource /mappers/sysuser-mapper.xml at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64) at com.javasm.TestGetById.test1_getUserById(TestGetById.java:21) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource /mappers/sysuser-mapper.xml at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120) at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78) ... 24 more Caused by: java.io.IOException: Could not find resource /mappers/sysuser-mapper.xml at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:114) at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:100) at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:366) at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:118) ... 26 more Process finished with exit code -1
要么看最上面
org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession. ### The error may exist in /mappers/sysuser-mapper.xml ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource /mappers/sysuser-mapper.xml
找到
Could not find resource /mappers/sysuser-mapper.xml
要么看下面,最后一个Cause by。
找不到映射文件,路径写错了。
在配置文件中将路径修改
<!--引入映射文件--> <mappers> <mapper resource="mappers/sysuser-mapper.xml"></mapper> </mappers>
开头不需要加/
再次打断点运行
发现数据库里有下划线的字段,虽然在数据库有数据,但在idea没有数据。
6.从SqlSessionFactory中获取SqlSession
见5
7.执行curd操作
见5
3.核心对象(***)
SqlSessionFactoryBuilder,此对象一般称为构建器对象,是构建器模式的应用.
此对象new出来仅仅执行一次build方法,加载xml配置文件输入流,创建出SqlSessionFactory后,即可销毁. 构建器模式:是GOF设计模式中的一种,作用来用来创建其他的单例类型的对象 XXXXBuilder{ YYYYbuild(){ } }
SqlSessionFactory,把该对象理解成DateSource连接池
此对象持有Configuration对象,configuration中包含Environment对象,包含数据库连接池.。 此对象持有Configuration对象,configuration中MappedStatement对象的集合,以namespace.id作为key,以MappedStatement来封装select标签的所有数据作为value.。 此对象生命周期:全局唯一 此对象常用方法:openSession
SqlSession:此对象类比成Connection连接对象
此对象生命周期:每次进行数据库操作,都需要临时创建的对象,用完执行close进行销毁. 此对象常用方法:selectOne,selectList,insert,delete,update,getMapper
23中GOF设计模式:单例模式,构建器模式.原型模式,工厂模式,访问者,观察者,适配器.代理...
4.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> <!--配置数据源。--> <environments default="704aDev"> <!--一个environment代表一个数据库的连接配置--> <environment id="704aDev"> <!--JDBC:启用事务控制 或者MANAGED:没有回滚概念,每次操作直接到数据库--> <transactionManager type="JDBC"/> <!--POOLED:mybatis内部提供了PooledDataSource对象连接池 UNPOOLED:mybatis不提供连接池 JNDI:mybatis不提供连接池,也不临时打开Connection,由第三方的tomcat来管理数据库连接池,然后 代码中通过命名目录服务jndi来获取第三方容器中的连接池。该技术已淘汰 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/704a"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--引入映射文件--> <mappers> <mapper resource="mappers/sysuser-mapper.xml"></mapper> </mappers> </configuration>
配置文件其他常用标签(官网可查)
1.settings标签
<!--settings标签是mybatis的调整设置标签,会改变mybatis运行时的行为--> <settings> <!--向控制台打印sql语句--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
向右的箭头是输入的东西。
向左的箭头是返回的结果。
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--开启数据库下划线命名自动映射到实体类的驼峰命名--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
此时,数据库中有下划线的字段,对应数据就能传到实体类。
2.properties标签
点开dtd文件
properties标签的顺序在settings标签前,故应在settings标签前打properties标签,这样才会有提示。(顺序不对也会报错)
<!--用来加载XXX.properties配置文件--> <properties resource="jdbc.properties"></properties>
在resources目录下新建jdbc.properties
(配置文件里不写这些数据,而另建立该文件是因为xml文件里不能打特殊符号)
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/704a jdbc.username=root jdbc.password=root
前面加jdbc是为了防止只写一个单词,而该单词是框架里的关键字,导致冲突。另一个原因,后续配置量会越来越大,这四个都是jdbc连接信息,归类便于配置。
对应的配置文件中配置数据源部分修改
<!--配置数据源。--> <environments default="704aDev"> <environment id="704aDev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments>
3.typeAlias别名标签
<!--类型别名标签--> <typeAliases> <!--为此类取一个别名--> <typeAlias type="com.javasm.entity.SysUser" alias="sysuser"></typeAlias> </typeAliases>
对应映射文件中,resultType直接写类名的别名即可。
<mapper namespace="aa.bb"> <!--parameterType可省略,刚开始用mybatis不建议省略,resultType不可省略--> <select id="getById" parameterType="java.lang.Integer" resultType="sysuser"> select * from sys_user where id =#{zzzzz} </select> <select id="getByName" parameterType="java.lang.String" resultType="sysuser"> select * from sys_user where uname like concat('%',#{sss}) </select> </mapper>
但不推荐该方式,因为一个项目的实体类很多,指定到上层包名即可
<!--类型别名标签--> <typeAliases> <!--类名作为别名,不用包名,同时忽略大小写--> <package name="com.javasm"/> </typeAliases>
此方式效果同上一个方式,但不需要每个类名都写别名。
忽略大小写,指resultType标签那里,sysuser可以是sYsuser等等。底层统一转小写。
但一定不要出现同名的类。
(mybatis里不支持通配的方式,mybatisPlus里支持,这里只是举例说明)
一般第三层包名是模块名 ,模块下的包名会保持一致。(即系统模块和订单模块都会有实体类entity)
如果两个模块的entity包下的类同名,则报错
此时配置文件如下:
<?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> <!--用来加载XXX.properties配置文件--> <properties resource="jdbc.properties"></properties> <!--调整运行时行为--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--类型别名标签--> <typeAliases> <package name="com.javasm"/> </typeAliases> <!--配置数据源。--> <environments default="704aDev"> <environment id="704aDev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--引入映射文件--> <mappers> <mapper resource="mappers/sysuser-mapper.xml"></mapper> </mappers> </configuration>
5.xml映射文件(***)
1.模糊查询
使用concat函数进行字符串拼接;也可以使用"%"进行拼接
(sql语句里都是单引号,像双引号这种不是正常用法)
<select id="getByName" parameterType="java.lang.String" resultType="com.javasm.entity.SysUser"> select * from sys_user where uname like "%"#{ddd} </select>
<select id="getByName" parameterType="java.lang.String" resultType="com.javasm.entity.SysUser"> select * from sys_user where uname like concat('%',#{sss},'%') </select>
从官网可查到参数类型对应的简化写法,叫内嵌别名
故,此时的映射文件为:
<?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"> <!--有namespace属性,namespace理解成dao类名,不同的映射文件namespace不能重复--> <mapper namespace="aa.bb"> <!--parameterType可省略,刚开始用mybatis不建议省略,resultType不可省略--> <select id="getById" parameterType="int" resultType="sysuser"> select * from sys_user where id =#{zzzzz} </select> <select id="getByName" parameterType="string" resultType="sysuser"> select * from sys_user where uname like concat('%',#{sss}) </select> </mapper>
2.select
id:在同一个映射文件,不能重名.不可省略。
parameterType:可省略 。
resultType:不可省略。此属性值有以下情况:实体类名,简单类型(String,Date,基本类型),map。绝对不能出现List,set。
(简单类型是框架里的说法,可直接使用的类型都叫简单类型)
根据id查电话,没必要将结果封装到sysuser对象。
<select id="getUphoneById" parameterType="int" resultType="string"> select uphone from sys_user where id =#{zzzzz} </select>
resultType的值取决于要查的值是什么类型。
<select id="getMapDatasById" parameterType="int" resultType="map"> select * from sys_user where id =#{zzzzz} </select>
列名作为key,值作为value。
增加数据库记录
还是查含t的用户名的记录数,此时用selectOne方法不合适。
<select id="getByName" parameterType="string" resultType="sysuser"> select * from sys_user where uname like concat('%',#{sss},'%') </select>
public class TestMapperXml { @Test public void getByName() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); List<Object> t = session.selectList("aa.bb.getByName", "t"); session.close(); } }
结果是用户集合,但resultType确是sysuser,resultType类比成返回值类型,只是类比。
resultType表示把单行记录封装的目标类型,故不能是List,set。
3.insert
<!--insert没有resultType属性--> <insert id="save" parameterType="sysuser"> insert into sys_user(uname,upwd,uphone,uwechat,uemail) values (#{uname},#{upwd},#{uphone},#{uwechat},#{sdept.dname}) </insert>
@Test public void save() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); SysUser u = new SysUser(); u.setUname("XXXX"); u.setUpwd("111"); u.setUphone("1111"); u.setUwechat("xxx333"); //返回受影响行数 int rows = session.insert("aa.bb.save", u); session.commit(); session.close(); }
4.update
<!--update没有resultType属性--> <update id="update" parameterType="sysuser"> update sys_user set upwd=#{upwd},uphone=#{uphone} where id=#{id} </update>
@Test public void update1() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); SysUser u = new SysUser(); u.setId(1); u.setUpwd("111"); u.setUphone("1111"); //返回受影响行数 int rows = session.update("aa.bb.update", u); session.commit(); session.close(); }
<update id="update2" parameterType="map"> update sys_user set upwd=#{a},uphone=#{b} where id=#{c} </update>
@Test public void update2() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); Map<String,Object> map = new HashMap<>(); map.put("a","ddd"); map.put("b","xxx"); map.put("c",1); //返回受影响行数 int rows = session.update("aa.bb.update2", map); session.commit(); session.close(); }
5.delete
<delete id="delByID" parameterType="int"> delete from sys_user where id=#{id} </delete>
@Test public void delete() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); int rows = session.delete("aa.bb.delByID", 8); session.commit(); session.close(); }
6.#{}
#{}表示mybatis使用PrepareStatement预编译对象进行sql编译,传入动态参数.
大括号中出现值有以下情况:随意,实体类的成员变量名,map的key值.
1.如果传入的参数是简单类型,则随便写. 2.如果传入的参数是实体对象,则写成员变量名. 3.如果传入的参数是Map,则写map的key值.
参考insert标签,传入的参数是实体对象,则写成员变量名。
参考update标签,传入的参数是Map,则写map的key值。
也可以是嵌套模式(前后端交互很常见)
entity下新建SysDept
public class SysDept { private Integer id; private String dname; //set get }
在SysUser里加入该字段
private SysDept sdept; //set get
若前端传入的用户数据包含部门名称数据,将该名称填入邮箱字段:
可在SysDept加入部门名称的有参构造。(加了有参构造就加无参构造,防止覆盖出错)
<insert id="save" parameterType="sysuser"> insert into sys_user(uname,upwd,uphone,uwechat,uemail) values (#{uname},#{upwd},#{uphone},#{uwechat},#{sdept.dname}) </insert>
@Test public void save() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); SysUser u = new SysUser(); u.setUname("XXXX"); u.setUpwd("111"); u.setUphone("1111"); u.setUwechat("xxx333"); u.setSdept(new SysDept("财务部")); //返回受影响行数 int rows = session.insert("aa.bb.save", u); session.commit(); session.close(); }
7.大于小于
大于 大于等于符号可以出现在sql语句
小于不行,因为<和标签的尖括号冲突。<是xml标签里特有的符号(html里也这样写,是类xml语言)
<select id="getUsers" parameterType="int" resultType="sysuser"> select * from sys_user where id <= #{id} </select>
@Test public void getUser() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); List<Object> objects = session.selectList("aa.bb.getUsers", 7); session.commit(); session.close(); }
6.getMapper方法(***.难点)
难是底层理解难。用起来简单。
1.添加dao接口
2.在映射文件的namespace值:namespace必须是dao接口的包名.类名
重新建立一个映射文件
<?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="com.javasm.sys.dao.SysUserDao"> <!-- SysUser getById(Integer id);--> <select id="getById" parameterType="integer" resultType="sysuser"> select * from sys_user where id=#{id} </select> <!-- List<SysUser> getUsers();--> <select id="getUsers" resultType="sysuser"> select * from sys_user </select> <!-- int save(SysUser u);--> <insert id="save"> insert into sys_user (uname,uphone,upwd) values(#{uname},#{uphone},#{upwd}) </insert> <!-- int update(SysUser U);--> <update id="update"> update sys_user set uname=#{uname},upwd=#{upwd} where id=#{id} </update> <!-- int del(Integer id);--> <delete id="del"> delete from sys_user where id=#{id} </delete> </mapper>
将映射文件引入到配置文件
<mappers> <mapper resource="mappers/sysuser-mapper.xml"></mapper> <mapper resource="mappers/sysuser-mapper2.xml"></mapper> </mappers>
新建测试类TestGetMapper
public class TestGetMapper { @Test public void TestGetMapper() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); //没有实现类,却得到了Dao接口的对象。有了该对象就能调用它的方法 SysUserDao ud = session.getMapper(SysUserDao.class); SysUser sysUser = ud.getById(1); List<SysUser> users = ud.getUsers(); session.close(); } }
3.映射文件的select|insert|update|delte标签的id必须是dao接口的方法名.
见2
7.多参数传递(***)
1.把多个参数封装到实体对象
见inseret,update
2.把多个参数封装到map对象
见update
3.在dao接口的方法中定义多个形参
根据用户名,密码查询
在dao接口增加
SysUser getByUnameAndId(String uname,Integer id); //mybatis底层操作永远只支持一个参数传递。传入多个参数,底层会把多个参数封装成Map //key为0,1,表示第一个参数,第二个参数或者param1,param2
在映射文件
<!--parameter写的话,类型是map--> <select id="getByUnameAndId" parameterType="map" resultType="sysuser"> select * from sys_user where id=#{1} and uname=#{0} </select>
或者
<!--parameter写的话,类型是map--> <select id="getByUnameAndId" parameterType="map" resultType="sysuser"> select * from sys_user where id=#{param2} and uname=#{param1} </select>
在测试类
@Test public void getByUnameAndPwd() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); SysUserDao ud = session.getMapper(SysUserDao.class); SysUser s = ud.getByUnameAndId("fyt", 4); session.close(); }
或者
通过注解自己生成key
dao接口
SysUser getByUnameAndId(@Param("un") String uname, @Param("ui") Integer id);
映射文件
<select id="getByUnameAndId" resultType="sysuser"> select * from sys_user where id=#{ui} and uname=#{un} </select>
8.常见异常
java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for aa.bb.getById 执行session对象的方法时,传入的namespace.id不存在。 (可能写错,可能未引入映射文件)
ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer 传入的参数类型与映射文件中parameterType的类型不一致
IllegalArgumentException: Mapped Statements collection already contains value for aa.bb.getById namespace.id重复
ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'aa.bb.getById'. It's likely that neither a Result Type nor a Result Map was specified. select标签没有resultType或者resultMap属性进行结果映射.
ReflectionException: There is no getter for property named 'a' in 'class com.javasm.sys.entity.SysUser' #{}括号中值写错
BindingException: Invalid bound statement (not found): com.javasm.sys.dao.SysuserDao.getByName dao接口中的方法在映射文件中没有对应的标签id
9.注意事项
1.idea不允许创建project,再创N个Module.maven聚合工程中才这样使用.
2..iml与.idea隐藏了,不上传svn;
一个.iml文件就是一个modules。
3.设置idea的encoding编码:UTF-8.防止乱码
4.数据库字段的date(年月日),datetime(年月日时分秒),timestamp
实体类变量类型对应使用Date或者String。原生jdbc中,用Date合适,在orm框架中,用String合适,因为Date序列化json字符串需要指定日期格式.
5.free mybatis plugin
mybatisX,自动让dao接口和映射文件对应。
10总结:
配置文件只需要知道settings标签,日志,驼峰映射.
映射文件中全部.(#{}与传入的参数.select的返回值)
dao接口
getMapper方法
三个核心对象
#{}写法
参数写法
select 标签的返回值
11.事务相关知识补充
1、事务
事务是指作为一个逻辑工作单元执行的一系列操作,这些操作要么全部成功,要么全部失败。事务确保了多个数据的修改作为一个单元来处理。
假如,张三在ATM机上给李四转账100元,在银行的业务系统中,主要会执行两步数据变更操作:
- 从张三的账户减去100元
- 给李四的账户增加100元
(简单的说,就是一个完整的事件集合)
2、提交事务
Commit Transaction,成功的结束,将所有的DML语句操作历史记录和底层硬盘数据来一次同步(增删改,查询不需要)
conn.commit();
3、事务回滚
End Transaction,失败的结束,将所有的DML(insert、update、delete)语句操作历史记录全部清空。
所谓回滚事务,简单来说就是当一个事务的某一个操作发生问题时,整个事务可以回滚掉,就像没有做任何操作一样,换言之当发生错误或事务被取消,则回滚事务。
当开启事务之后,所有的操作都会被存储在事务日志中,而只有当我们进行提交事务的操作后,才会将我们更新的数据同步到数据表中。
conn.rollback()
4.事务控制:
在默认情况下,MySQL是自动提交事务的,即每一条INSERT、UPDATE、DELETE的SQL语句提交后会立即执行COMMIT操作。一旦命令被提交,就无法对它进行回滚操作。在使用事务时,需要关闭这个默认值:
conn.setAutoCommit(false); (使用事务时,需要关闭这个默认值)
5.事务关闭
所有操作执行完,需要将连接归还连接池。
coon.close()
(执行该操作前应该conn.setAutoCommit(true);恢复默认的自动提交)
相关例子:
为确保事务中的连接是同一个,可将connection设为全局变量(跨线程,跨类),但这会导致线程安全问题。
ThreadLocal,缩小共享范围,可使全局共享变量成为相互隔离的线程内部的共享变量。
这里保证一个线程共享一个连接。
ThreadLocal<Connection> connThreadLocal =new ThreadLocal<>(); //线程锁,只在当前线程可用的东西 Connection connection = connThreadLocal.get();
与数据库的连接------connection怎么拿?
若jar包只有类似mysql-connector-java-5.1.20-bin.jar的情况:
Connection con=DriverManager.getConnection(url:"jdbc:mysql://localhost:3306/704a", user:"root", password:"root");
具体可参考:
Java怎么连接数据库-java教程-PHP中文网https://www.php.cn/java-article-418073.html
(这个没加回滚和关闭)
以上是原生的jdbc连接数据库方式。
可以优化-------再加个jdbc.properties的配置文件。
若再引入druid(数据库连接池),多了个druid-1.0.9.jar包后,使用时,先得获取连接池对象,再获取连接对象。
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); Connection connection = dataSource.getConnection();
写的再详细点,或者说若使用DruidDataSource这个对象时,那就先初始化它,一般都是:
public DruidDataSource druidDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(url); druidDataSource.setDriverClassName(driver); druidDataSource.setUsername(username); druidDataSource.setPassword(password); druidDataSource.setInitialSize(initialSize); return druidDataSource; }
然后:
Connection con= druidDataSource.getConnection();
(一个是DataSource,一个是DruidDataSource,这两用法不一样!)
到了框架这一部分,用mybaties的配置文集引入jdbc.properties的配置文件,核心操作都是一样的。