化妆品公司网站源码,网络宣传的方法,海东高端网站建设,网页设计制作项目文章目录 Mybatis基础XML语言概述使用Mybatis配置Mybatis增删改查复杂查询事务操作动态 SQLifchoose、when、otherwise 缓存机制注解开发 Mybatis基础
虽然我们能够通过JDBC来连接和操作数据库#xff0c;但是哪怕只是完成一个SQL语句的执行#xff0c;都需要编写大量的代码… 文章目录 Mybatis基础XML语言概述使用Mybatis配置Mybatis增删改查复杂查询事务操作动态 SQLifchoose、when、otherwise 缓存机制注解开发 Mybatis基础
虽然我们能够通过JDBC来连接和操作数据库但是哪怕只是完成一个SQL语句的执行都需要编写大量的代码更不用说如果我还需要进行实体类映射将数据转换为我们可以直接操作的实体类型JDBC很方便但是还不够方便。
MyBatis 是一款优秀的持久层框架它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
XML语言概述
XML语言发明最初是用于数据的存储和传输
HTML主要用于通过编排来展示数据而XML主要是存放数据它更像是一个配置文件
?xml version1.0 encodingUTF-8 ?
outername阿伟/namedesc怎么又在玩电动啊/descinner type1age10/agesex男/sex/inner
/outer一个XML文件存在以下的格式规范
必须存在一个根节点将所有的子标签全部包含可以但不必须包含一个头部声明主要是可以设定编码格式所有的标签必须成对出现可以嵌套但不能交叉嵌套区分大小写标签中可以存在属性属性的值由单引号或双引号包括
XML文件也可以使用注释
?xml version1.0 encodingUTF-8 ?
!-- 注释内容 --内容中出现特定字符需要使用转义字符 使用CD来快速创建不解析区域
testname![CDATA[我看你是一点都不懂哦]]/name
/test使用Mybatis
导入Mybatis的依赖
编写Mybatis的配置文件在项目根目录下新建名为mybatis-config.xml
?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd
configurationenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver value${驱动类含包名}/property nameurl value${数据库连接URL}/property nameusername value${用户名}/property namepassword value${密码}//dataSource/environment/environments
/configuration通过进行配置告诉了Mybatis我们链接数据库的一些信息包括URL、用户名、密码等
Mybatis对配置文件进行读取并得到一个SqlSessionFactory对象
public static void main(String[] args) throws FileNotFoundException {SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(new FileInputStream(mybatis-config.xml));try (SqlSession sqlSession sqlSessionFactory.openSession(true)){//暂时还没有业务}
}每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的可以通过SqlSessionFactory来创建多个新的会话
配合lombok编写实体类
import lombok.Data;Data
public class Student {int sid; //名称最好和数据库字段名称保持一致不然可能会映射失败导致查询结果丢失String name;String sex;
}在根目录下重新创建对象映射器文件TestMapper.xml
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespaceTestMapperselect idselectStudent resultTypecom.test.entity.Studentselect * from student/select
/mapper其中namespace就是命名空间每个Mapper都是唯一的因此需要用一个命名空间来区分它还可以用来绑定一个接口。
写入了一个select标签表示添加一个select操作同时id作为操作的名称resultType指定为我们刚刚定义的实体类表示将数据库结果映射为Student类然后就在标签中写入我们的查询语句即可。
在配置文件中添加这个Mapper映射器
mappersmapper urlfile:mappers/TestMapper.xml/!-- 这里用的是url也可以使用其他类型 --
/mappers在程序中使用定义好的Mapper
public static void main(String[] args) throws FileNotFoundException {SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(new FileInputStream(mybatis-config.xml));try (SqlSession sqlSession sqlSessionFactory.openSession(true)){ListStudent student sqlSession.selectList(selectStudent);student.forEach(System.out::println);}
}Mybatis非常智能只需要告诉一个映射关系就能够直接将查询结果转化为一个实体类
配置Mybatis
由于SqlSessionFactory一般只需要创建一次因此我们可以创建一个工具类来集中创建SqlSession这样会更加方便一些
public class Mybatis {private static SqlSessionFactory sqlSessionFactory;static {try {sqlSessionFactory new SqlSessionFactoryBuilder().build(new FileInputStream(mybatis-config.xml));} catch (FileNotFoundException e) {throw new RuntimeException(e);}}public static SqlSession getSession(boolean autoCommit) {return sqlSessionFactory.openSession(autoCommit);}
}public static void main(String[] args) {try (SqlSession sqlSession MybatisUtil.getSession(true)){ListStudent student sqlSession.selectList(selectStudent);student.forEach(System.out::println);}
}每次都需要去找映射器对应操作的名称而且还要知道对应的返回类型再通过SqlSession来执行对应的方法能不能再方便一点呢
通过namespace来绑定到一个接口上利用接口的特性我们可以直接指明方法的行为而实际实现则是由Mybatis来完成。
public interface TestMapper {ListStudent selectStudent();
}将Mapper文件的命名空间修改为我们的接口建议同时将其放到同名包中作为内部资源
mapper namespacecom.test.mapper.TestMapperselect idselectStudent resultTypecom.test.entity.Studentselect * from student/select
/mapper修改一下配置文件中的mapper定义不使用url而是resource表示是Jar内部的文件
mappersmapper resourcecom/test/mapper/TestMapper.xml/
/mappers可以直接通过SqlSession获取对应的实现类通过接口中定义的行为来直接获取结果
public static void main(String[] args) throws FileNotFoundException {try(SqlSession session Mybatis.getSession(true)) {//自动提交TestMapper testMapper session.getMapper(TestMapper.class);ListStudent selectStudent testMapper.selectStudent();selectStudent.forEach(System.out::println);}
}TestMapper是通过动态代理生成的相当于动态生成了一个实现类而不是预先定义好的
配置文件介绍
configurationenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurl valuejdbc:mysql://localhost:3306/study/property nameusername valuetest/property namepassword value123456//dataSource/environment/environmentsmappersmapper resourcecom/test/mapper/TestMapper.xml//mappers
/configurationenvironments标签指定一个数据库的配置信息包含连接URL、用户、密码等信息
实际情况下可能会不止有一个数据库连接信息比如开发过程中我们一般会使用本地的数据库而如果需要将项目上传到服务器或是防止其他人的电脑上运行时我们可能就需要配置另一个数据库的信息
在environments标签上有一个default属性来指定默认的环境
也可以在创建工厂时选择环境
sqlSessionFactory new SqlSessionFactoryBuilder().build(new FileInputStream(mybatis-config.xml), 环境ID);可以给类型起一个别名:
!-- 需要在environments的上方 --
typeAliasestypeAlias typecom.test.entity.Student aliasStudent/
/typeAliases直接让Mybatis去扫描一个包并将包下的所有类自动起别名别名为首字母大小写都行的类名:
typeAliasespackage namecom.test.entity/
/typeAliases也可以为指定实体类添加一个注解来指定别名
Data
Alias(lbwnb)
public class Student {private int sid;private String name;private String sex;
}增删改查
resultType可以被映射到一个Map上
select idselectStudent resultTypeMapselect * from student
/selectpublic interface TestMapper {ListMap selectStudent();
}Map中就会以键值对的形式来存放这些结果了
通过设定一个resultType属性让Mybatis知道查询结果需要映射为哪个实体类要求字段名称保持一致。
自定义resultMap来设定映射规则
resultMap idTest typeStudentresult columnid propertyid/result columnname propertyname/
/resultMap
select idselectStudent resultMapTestselect * from student
/selectcolumn表示数据库字段名称property表示实体类字段名称
如果一个类中存在多个构造方法那么很有可能会出现错误
使用constructor标签来指定构造方法
resultMap idtest typeStudentconstructorarg columnsid javaTypeInteger/arg columnname javaTypeString//constructor
/resultMap指定构造方法后若此字段被填入了构造方法作为参数将不会通过反射给字段单独赋值而构造方法中没有传入的字段依然会被反射赋值
如果只有一个构造函数或者全量构造函数都会先调用一遍构造函数然后使用反射进行字段单独赋值
数据库中存在一个带下划线的字段我们可以通过设置让其映射为以驼峰命名的字段比如my_test映射为myTest
settingssetting namemapUnderscoreToCamelCase valuetrue/
/settings条件查询想通过sid字段来通过学号查找信息
Student getStudentBySid(int sid);select idgetStudentBySid parameterTypeint resultTypeStudentselect * from student where sid #{sid}
/select通过使用#{xxx}或是${xxx}来填入我们给定的属性实际上Mybatis本质也是通过PreparedStatement首先进行一次预编译有效地防止SQL注入问题但是如果使用${xxx}就不再是通过预编译而是直接传值因此我们一般都使用#{xxx}来进行操作。
使用parameterType属性来指定参数类型非必须可以不用推荐不用
插入、更新和删除操作
insert idaddStudent parameterTypeStudentinsert into student(name, sex) values(#{name}, #{sex})
/insertint addStudent(Student student);复杂查询
Data
public class Teacher {int tid;String name;ListStudent studentList;
}一个老师可以教授多个学生那么能否一次性将老师的学生全部映射给此老师的对象呢
映射为Teacher对象时同时将其教授的所有学生一并映射为List列表显然这是一种一对多的查询那么这时就需要进行复杂查询了。
现在需要使用resultMap来自定义映射规则
select idgetTeacherByTid resultMapasTeacherselect *, teacher.name as tname from student inner join teach on student.sid teach.sidinner join teacher on teach.tid teacher.tid where teach.tid #{tid}
/selectresultMap idasTeacher typeTeacherid columntid propertytid/result columntname propertyname/collection propertystudentList ofTypeStudentid columnsid propertysid/result columnname propertyname/result columnsex propertysex//collection
/resultMapid标签用于在多条记录中辨别是否为同一个对象的数据
通过使用collection来表示将得到的所有结果合并为一个集合比如上面的数据中每个学生都有单独的一条记录因此tid相同的全部学生的记录就可以最后合并为一个List得到最终的映射结果当然为了区分最好也设置一个id只不过这个例子中可以当做普通的result使用。
Data
Accessors(chain true)
public class Student {private int sid;private String name;private String sex;private Teacher teacher;
}Data
public class Teacher {int tid;String name;
}可以使用resultMap来实现
resultMap idtest2 typeStudentid columnsid propertysid/result columnname propertyname/result columnsex propertysex/association propertyteacher javaTypeTeacherid columntid propertytid/result columntname propertyname//association
/resultMap
select idselectStudent resultMaptest2select *, teacher.name as tname from student left join teach on student.sid teach.sidleft join teacher on teach.tid teacher.tid
/select事务操作
获取SqlSession关闭自动提交来开启事务模式
public static void main(String[] args) {try (SqlSession sqlSession MybatisUtil.getSession(false)){TestMapper testMapper sqlSession.getMapper(TestMapper.class);testMapper.addStudent(new Student().setSex(男).setName(小王));testMapper.selectStudent().forEach(System.out::println);}
}提交事务
sqlSession.commit();事务回滚
sqlSession.rollback();动态 SQL
动态 SQL 是 MyBatis 的强大特性之一。可以根据不同条件拼接 SQL 语句
if
select idfindActiveBlogWithTitleLikeresultTypeBlogSELECT * FROM BLOGWHERE state ‘ACTIVE’if testtitle ! nullAND title like #{title}/if
/select如果不传入 “title”那么所有处于 “ACTIVE” 状态的 BLOG 都会返回如果传入了 “title” 参数那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果
choose、when、otherwise
有时候我们不想使用所有的条件而只是想从多个条件中选择一个使用。针对这种情况MyBatis 提供了 choose 元素它有点像 Java 中的 switch 语句。
select idfindActiveBlogLikeresultTypeBlogSELECT * FROM BLOG WHERE state ‘ACTIVE’choosewhen testtitle ! nullAND title like #{title}/whenwhen testauthor ! null and author.name ! nullAND author_name like #{author.name}/whenotherwiseAND featured 1/otherwise/choose
/select缓存机制
Mybatis内置了一个缓存机制我们查询时如果缓存中存在数据那么我们就可以直接从缓存中获取而不是再去向数据库进行请求。
Mybatis存在一级缓存和二级缓存默认情况下只启用了本地的会话缓存它仅仅对一个会话中的数据进行缓存一级缓存无法关闭只能调整 一级缓存在进行DML操作后会使得缓存失效。
当前会话结束后也会清理全部的缓存因为已经不会再用到了。
一级缓存只针对于单个会话多个会话之间不相通。
public static void main(String[] args) throws InterruptedException {try (SqlSession sqlSession MybatisUtil.getSession(true)){TestMapper testMapper sqlSession.getMapper(TestMapper.class);Student student1 testMapper.getStudentBySid(1);Student student2 testMapper.getStudentBySid(1);System.out.println(student1 student2);}
}一级缓存给我们提供了很高速的访问效率但是它的作用范围实在是有限如果一个会话结束那么之前的缓存就全部失效了
如果希望缓存能够扩展到所有会话都能使用可以通过二级缓存来实现
二级缓存默认是关闭状态要开启二级缓存需要在映射器XML文件中添加
cache/cacheevictionFIFO //缓存淘汰策略flushInterval60000 //缓存刷新间隔size512 //缓存个数readOnlytrue/ //只读缓存如果我不希望某个方法开启缓存呢我们可以添加useCache属性来关闭缓存
select idgetStudentBySid resultTypeStudent useCachefalseselect * from student where sid #{sid}
/select可以使用flushCachefalse在每次执行后都清空缓存通过这这个我们还可以控制DML操作完成之后不清空缓存。
select idgetStudentBySid resultTypeStudent flushCachetrueselect * from student where sid #{sid}
/select添加了二级缓存之后会先从二级缓存中查找数据当二级缓存中没有时才会从一级缓存中获取当一级缓存中都还没有数据时才会请求数据库
如果存在多台服务器或者是多个程序都在使用Mybatis操作同一个数据库并且都开启了缓存会存在缓存一致性问题此时得关闭Mybatis的缓存来保证一致性
settingssetting namecacheEnabled valuefalse/
/settingsselect idgetStudentBySid resultTypeStudent useCachefalse flushCachetrueselect * from student where sid #{sid}
/select注解开发
直接使用注解来实现XML中定义映射规则和SQL语句并绑定到一个接口的方法定义上
Insert(insert into student(name, sex) values(#{name}, #{sex}))
int addStudent(Student student);修改一下配置文件中的映射器注册
mappersmapper classcom.test.mapper.MyMapper/!-- 也可以直接注册整个包下的 package namecom.test.mapper/ --
/mappers进行自定义映射规则
Results({Result(id true, column sid, property sid),Result(column sex, property name),Result(column name, property sex)
})
Select(select * from student)
ListStudent getAllStudent();Result注解数组每个Result注解都都一个单独的字段配置
复杂查询
Results({Result(id true, column tid, property tid),Result(column name, property name),Result(column tid, property studentList, many Many(select getStudentByTid))
})
Select(select * from teacher where tid #{tid})
Teacher getTeacherBySid(int tid);Select(select * from student inner join teach on student.sid teach.sid where tid #{tid})
ListStudent getStudentByTid(int tid);多出了一个子查询而这个子查询是单独查询该老师所属学生的信息而子查询结果作为Result注解的一个many结果代表子查询的所有结果都归入此集合中也就是之前的collection标签
Result也提供了One子注解来实现一对一的关系表示类似于之前的assocation标签
Results({Result(id true, column sid, property sid),Result(column sex, property name),Result(column name, property sex),Result(column sid, property teacher, one One(select getTeacherBySid))
})
Select(select * from student)
ListStudent getAllStudent();直接使用注解编写SQL语句但是我希望映射规则依然使用XML来实现提供了ResultMap注解直接指定ID即可
ResultMap(test)
Select(select * from student)
ListStudent getAllStudent();通过ConstructorArgs注解来指定构造方法
ConstructorArgs({Arg(column sid, javaType int.class),Arg(column name, javaType String.class)
})
Select(select * from student where sid #{sid} and sex #{sex})
Student getStudentBySidAndSex(Param(sid) int sid, Param(sex) String sex);当参数列表中出现两个以上的参数时添加Param来指定参数名称
Select(select * from student where sid #{sid} and sex #{sex})
Student getStudentBySidAndSex(Param(sid) int sid, Param(sex) String sex);通过参数名称.属性的方式去让Mybatis知道我们要用的是哪个属性
Insert(insert into student(sid, name, sex) values(#{sid}, #{student.name}, #{student.sex}))
int addStudent(Param(sid) int sid, Param(student) Student student);注解控制缓存机制
CacheNamespace(readWrite false)
public interface MyMapper {Select(select * from student)Options(useCache false)ListStudent getAllStudent();Sex(Param(“sid”) int sid, Param(“sex”) String sex); 当参数列表中出现两个以上的参数时添加Param来指定参数名称~~~java
Select(select * from student where sid #{sid} and sex #{sex})
Student getStudentBySidAndSex(Param(sid) int sid, Param(sex) String sex);通过参数名称.属性的方式去让Mybatis知道我们要用的是哪个属性
Insert(insert into student(sid, name, sex) values(#{sid}, #{student.name}, #{student.sex}))
int addStudent(Param(sid) int sid, Param(student) Student student);注解控制缓存机制
CacheNamespace(readWrite false)
public interface MyMapper {Select(select * from student)Options(useCache false)ListStudent getAllStudent();使用CacheNamespace注解直接定义在接口上即可然后我们可以通过使用Options来控制单个操作的缓存启用。