Mybatis框架
MyBatis框架
一、常用的几种框架
1.Mybatis 半自动化ORM框架 (hibernate全自动框架)
- Mybatis Plus
2.Spring SSM,SSH都离不开Spring
3.SpringMVC
4.SpringBoot
5.SpringCloud
6.Dubbo
二、Mybatis开篇
1.持久化和ORM
持久化:代表的数据从瞬时状态转换为持久状态的过程
ORM:对象关系映射,说白了就是程序实体与数据库二维表的字段映射,如果实体属性和表中字段一致,会自动将数据封装,如果不一致也有手动映射的方式
2.什么是半自动化?
不是全部有框架来完成工作,SQL有程序员自己编译,要求较高的SQL基础,
3.搭建Mybatis开发环境
步骤一:导入Mybatis依赖,数据库依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
步骤二:创建Mybatis大配置文件
配置文件内需要配置数据源,配置别名等等等的内容都是在该配置文件内
<?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="datasource.properties"></properties>
<!--defauilt代表默认情况下-->
<environments default="development">
<!--环境:可以有多套-->
<environment id="development">
<!--事务管理采用JDBC,需要程序员手动提交事务,否则数据会出现脏数据 type还可以取值为managerd 什么都不管-->
<transactionManager type="JDBC"/>
<!--数据源 type取值默认为pooled连接池,还可以采用unpooled 还有 JNDI方式-->
<dataSource type="POOLED">
<!--driver代表数据库驱动-->
<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>
<!--小配置文件映射集合,之前我们采用Dao层实现类编写SQL处理数据,学习了Mybatis就是一个Dao层接口对应一个小配置 文件, 将SQL放入到配置文件当中-->
<mappers>
<!--单独的小配置文件加载-->
<!--<mapper resource="com/wdksoft/dao/UserMapper.xml"/>-->
<!--小配置文件所在包名-->
<package name="com.wdksoft.dao"/>
</mappers>
</configuration>
步骤三:创建实体
步骤四:创建Dao层接口和小配置文件
public interface INewsUsersDao {
//查询所有用户信息
public List<News_Users> getAllUser() throws Exception;
}
小配置文件需要与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">
<!--namespace代表命名空间,指定小配置文件所关联的Dao层接口位置-->
<mapper namespace="com.wdksoft.dao.INewsUsersDao">
<!--查询所有员工记录 id代表方法名字 resultType代表返回类型 -->
<select id="getAllUser" resultType="com.wdksoft.entity.News_Users">
select * from news_users
</select>
</mapper>
默认情况下不会扫描java下的xml文件,所以解决方案为在pom.xml文件中加入节点扫描
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
步骤五:测试
//加载配置文件,以流的形式
//通过SqlSessionFactoryBuilder来获取工厂.build(is)
//通过工厂获取SqlSession
factory.openSession();
public static void main(String[] args) throws IOException {
//最终终极目的获取到SqlSession对象,因为该对象提供增伤改查方法
//步骤一:创建输入流,加载配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//步骤二:通过SqlSessionFactoryBuilder获取SqlSessionFactory工长
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//步骤三:通过工厂创建SqlSession对象
SqlSession sqlSession = build.openSession();
//步骤四:执行增删改查
List<News_Users> userList = sqlSession.selectList("getAllUser");
for(News_Users user:userList){
System.out.println(user.toString());
}
//最后一步:关闭资源
sqlSession.close();
}
4.别名使用
在大配置文件中,添加配置
<!--配置别名-->
<typeAliases>
<!--配置单独别名,默认规则时以实体命名-->
<!-- <typeAlias type="com.wdksoft.entity.News_Users" alias="User"/>-->
<!--全局配置采用实体命名-->
<package name="com.wdksoft.entity"/>
</typeAliases>
小配置使用实体名称即可
5.集成Log4j
1.将Log4j的配置文件添加到环境当中
2.需要引入log4j依赖
3.在Mybatis大配置文件中引用Log4j
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
6.GetMapper定位到具体接口
INewsUsersDao mapper = sqlSession.getMapper(INewsUsersDao.class);
List<News_Users> allUser = mapper.getAllUser();
for(News_Users user:allUser){
System.out.println(user.toString());
}
三、Mybatis增删改查
1、添加数据
步骤一:在接口中新建方法
//添加数据
public int addUser(News_Users users) throws Exception;
步骤二:在下配置文件中加入SQL
<!--添加数据-->
<insert id="addUser">
<!--#{}可以获取到基本数据类型的变量名,也可以获取负责数据类型中的属性名-->
insert into news_users(uname,upwd,telephone,email) values(#{uname},#{upwd},#{telephone},#{email})
</insert>
步骤三:测试,手动提交
@Test
public void insert() throws Exception {
//步骤一:将配置文件以流的形式加载
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//步骤二:创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//步骤三:获取SqlSession对象
SqlSession sqlSession = factory.openSession();
//步骤四:获取接口类型
INewsUsersDao iNewsUsersDao = sqlSession.getMapper(INewsUsersDao.class);
News_Users users=new News_Users();
users.setUname("chaiyingjie");
users.setUpwd("pwd@123");
users.setTelephone("13888888888");
users.setEmail("123@qq.com");
int count = iNewsUsersDao.addUser(users);
if(count>0){
System.out.println("添加成功~");
}else{
System.out.println("添加失败~");
}
//增伤改需要手动提交,SqlSession底层调用的还是transaction事务对象提交事务
sqlSession.commit(true);
//步骤五:关闭资源
sqlSession.close();
}
提示:
#{}表示一个占位符号 通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型 转换, #{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。 ${}表示拼接 sql 串 通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, 可以接 收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值, {}可以接收简单 类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,{}括号中只能是 value。
2、Mybaits修改案例
步骤一:在接口中新建方法
//修改数据
public int updateUser(News_Users users) throws Exception;
步骤二:配置小配置文件
<!--修改数据-->
<update id="updateUser">
update news_users set uname=#{uname},upwd=#{upwd} where uid=#{uid}
</update>
步骤三:测试
@Test
public void update() throws Exception {
//步骤一:将配置文件以流的形式加载
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//步骤二:创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//步骤三:获取SqlSession对象
SqlSession sqlSession = factory.openSession();
//步骤四:获取接口类型
INewsUsersDao iNewsUsersDao = sqlSession.getMapper(INewsUsersDao.class);
News_Users users=new News_Users();
users.setUname("yiming");
users.setUpwd("123456");
users.setUid(8);
int count = iNewsUsersDao.updateUser(users);
System.out.println(count);
//增伤改需要手动提交,SqlSession底层调用的还是transaction事务对象提交事务
sqlSession.commit(true);
//步骤五:关闭资源
sqlSession.close();
}
3、Mybatis删除案例
步骤一:在接口中新建方法
//删除数据
public int deleteUser(Integer uid) throws Exception;
步骤二:配置小配置文件
<!--删除数据-->
<delete id="deleteUser">
delete from news_users where uid=#{uid}
</delete>
步骤三:测试
@Test
public void delete() throws Exception {
//步骤一:将配置文件以流的形式加载
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//步骤二:创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//步骤三:获取SqlSession对象
SqlSession sqlSession = factory.openSession();
//步骤四:获取接口类型
INewsUsersDao iNewsUsersDao = sqlSession.getMapper(INewsUsersDao.class);
int count = iNewsUsersDao.deleteUser(10);
System.out.println(count);
//增伤改需要手动提交,SqlSession底层调用的还是transaction事务对象提交事务
sqlSession.commit(true);
//步骤五:关闭资源
sqlSession.close();
}
4、添加数据返回主键三种方案
第一种在insert标签内加入useGeneratedKeys代表开启JDBC生成主键 keyProperty代表实体主键属性名称
<insert id="addUser" useGeneratedKeys="true" keyProperty="uid">
insert into news_users(uname,upwd,telephone,email) values(#{uname},#{upwd},#{telephone},#{email})
</insert>
第二种在insert语句后查询最后一次添加数据主键的id
<selectKey keyProperty="uid" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID() AS UID
</selectKey>
第三种与第二种类似,查询主键全局变量
<selectKey keyProperty="uid" resultType="java.lang.Integer">
SELECT @@IDENTITY AS UID
</selectKey>
5、自动映射和ResultMap的使用
Mybatis默认是自动映射的,只要实体的属性和数据库字段一致就可以自动映射
当实体和数据库二维表不一致的情况下,或者是在关联查询的场景下我们使用resultMap
<!--resultMap结果映射,在多表连接查询时,在实体与数据库二维表字段不一致时 id代表resultMap结果的唯一标识 type 代表实体的类型-->
<resultMap id="userMap" type="News_Users">
<!--result标签代表普通字段 property代表的是实体中的属性 column代表数据库二维表中字段-->
<result property="phone" column="telephone"/>
</resultMap>
<select id="getAllUser" resultMap="userMap">
select * from news_users
</select>
取消全局映射:在Mybatis大配置文件中settings节点下加入autoMappingBehavior属性 属性值默认取值为PARTIAL代表只映射自己,在关联查询时不会映射其他实体数据 NONE代表取消映射 FULL所有都能映射
取消局部映射,在小配置文件的resultMap节点中加入autoMapping属性值为false,默认为true,关闭了映射之后所有的字段如果没有在resultMap手动映射,则取值为null
6、MybatisUtil工具类提炼
核心思想,利用静态代码块先创建出工厂,然后再获取SqlSession的方法内通过工厂获取对象
public class MybatisUtil {
static String resourcePath="mybatis-config.xml";
static InputStream is = null;
static SqlSessionFactory factory = null;
static {
try {
is = Resources.getResourceAsStream(resourcePath);
//获取工厂
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession对象
public static SqlSession getSqlSession(){
return factory.openSession();
}
//关闭SqlSession对象
public static void closeSqlSession(SqlSession session){
session.close();
}
}
7、调用测试:
public static void main(String[] args) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
//步骤四:执行增删改查
INewsUsersDao mapper = sqlSession.getMapper(INewsUsersDao.class);
List<News_Users> allUser = mapper.getAllUser();
for(News_Users user:allUser){
System.out.println(user.toString());
}
MybatisUtil.closeSqlSession(sqlSession);
}
三、模糊查询
<!--模糊查询-->
<select id="getLikeUser" resultType="News_Users">
1、<!--使用#{}-->
<!--select * from news_users where uname like '%' #{uname} '%'-->
2、<!--使用concat字符串拼接-->
select * from news_users where uname like concat('%',#{uname},'%')
3、<!--${}不能防止SQL注入,而且必须加@Param指定参数-->
<!--select * from news_users where uname like '%${uname}%'-->
</select>
四、多条件查询
1、使用@Param
当零散参数时,没有办法根据默认参数名进行装配,所以可以使用@Param指定名字
public List<News_Users> getUser(@Param("uname") String uname,@Param("email") String email) throws Exception;
<select id="getUser" resultType="News_Users">
select * from news_users where uname=#{uname} and email=#{email}
</select>
2、使用Map集合
将零散参数装配到map集合当中,但是注意map集合的key需要和小配置文件应用属性名称保持一致
public List<News_Users> getUserForMap(Map<String,Object> map) throws Exception;
小配置:
<select id="getUser" resultType="News_Users">
select * from news_users where uname=#{uname} and email=#{email}
</select>
测试类:
//将多条件封装到Map集合当中
Map<String,Object> map=new HashMap<>();
//key需要与小配置文件的参数名称一致
map.put("uname","chaiyingjie");
map.put("email","123@qq.com");*/
List<News_Users> userList = mapper.getUserForMap(map);
for (News_Users userItem:userList) {
System.out.println(userItem.toString());
}
3、使用下标
需要使用小标指定参数位置,arg小标(从0开始)
public List<News_Users> getUserFroIndex(String uname,String email) throws Exception;
小配置文件:
<select id="getUserFroIndex" resultType="News_Users">
select * from news_users where uname=#{arg0} and email=#{arg1}
</select>
五、智能标签,动态SQL
1、where智能标签
识别第一个and或者or关键字,变为where
2、if智能标签
可以动态判断条件,动态拼接SQL
3.where和if实现动态条件判断案例
//动态查询,用户可以随意组合条件,可以根据标题模糊查询,也可以根据分类查询
public List<News> getNews(@Param("ntitle") String ntitle, @Param("ntid") Integer ntid) throws Exception;
小配置文件:
<select id="getNews" resultType="News">
select * from news
<!--加入where,会将第一个and或者or变为where-->
<where>
<!--ntitle不用加#{} 并且用and 或者用or-->
<if test="ntitle!=null and ntitle!=''">
and ntitle like concat('%',#{ntitle},'%')
</if>
<if test="ntid!=0">
and ntid=#{ntid}
</if>
</where>
</select>
测试类:
@Test
public void getNewsData() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
INewsMapper mapper = sqlSession.getMapper(INewsMapper.class);
List<News> newsList = mapper.getNews("!", 4);
for(News news:newsList){
System.out.println(news.toString());
}
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
4、Choose
可以用于多条件判断,使用when与if相同。其他情况也已使用otherwise,只能执行一个
//查询新闻数据,如果用户指定新闻分类则查询特定新闻分类数据,如果没有指定,默认新闻分类为1
public List<News> getNewsByNtid(@Param("ntid") Integer ntid) throws Exception;
小配置文件:
<select id="getNewsByNtid" resultType="News">
select * from news
<where>
<choose>
<!--可以使用when条件判断-->
<when test="ntid!=0">
and ntid=#{ntid}
</when>
<!--否则情况-->
<otherwise>
and ntid=1
</otherwise>
</choose>
</where>
</select>
测试类:
@Test
public void getNewsByNtid() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
INewsMapper mapper = sqlSession.getMapper(INewsMapper.class);
List<News> newsList = mapper.getNewsByNtid(4);
for(News news:newsList){
System.out.println(news.toString());
}
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
5、Set标签
主要用于修改,可以加入set关键字,也能忽略最后一个,
//动态修改
public int updateNews(@Param("ntitle") String ntitle,@Param("ntid") Integer ntid,@Param("nid")Integer nid) throws Exception;
<!--动态修改-->
<update id="updateNews">
update news
<!--给第一个参数前加入set关键字,能够忽略最后一个,-->
<set>
<if test="ntitle!=null and ntitle!=''">
ntitle=#{ntitle},
</if>
<if test="ntid!=0">
ntid=#{ntid},
</if>
</set>
where nid=#{nid}
</update>
@Test
public void updateNews() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
INewsMapper mapper = sqlSession.getMapper(INewsMapper.class);
int count = mapper.updateNews("新管状病毒", 4, 170);
System.out.println(count);
sqlSession.commit();
//update news where nid=170
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
6、Trim
可以加入前缀和后缀,也可以替换,删除前缀后缀
Prefix:在结果前加入一个前缀 PrefixOverrides:替换或者覆盖或者删除掉前缀 Suffix:在结果后加入一个后缀 SuffixOverrides:替换或者覆盖或者删除掉后缀
1.使用Trim更改where动态条件拼接
<select id="getNews" resultType="News">
select * from news
<!--trim prefix代表添加一个前缀 prefixOverrides将and|or替换 只替换第一个-->
<trim prefix="where" prefixOverrides="AND|OR">
<!--ntitle不用加#{} 并且用and 或者用or-->
<if test="ntitle!=null and ntitle!=''">
and ntitle like concat('%',#{ntitle},'%')
</if>
<if test="ntid!=0">
and ntid=#{ntid}
</if>
</trim>
</select>
2.替代set
<update id="updateNews">
update news
<!--给第一个参数前加入set关键字,能够忽略最后一个,-->
<trim prefix="set" suffixOverrides=",">
<if test="ntitle!=null and ntitle!=''">
ntitle=#{ntitle},
</if>
<if test="ntid!=0">
ntid=#{ntid},
</if>
</trim>
where nid=#{nid}
</update>
7、Foreach
可以遍历集合或者数组
//查询发布新闻作者为sprot和news的新闻
public List<News> getNewsByAuthor(@Param("authors") String [] authors) throws Exception;
<select id="getNewsByAuthor" resultType="news">
select * from news where nauthor in
<!--collection代表数组/集合 也能代表名称(需要@Param指定名称) open代表前缀 close代表后缀 separator每一项分隔符 item代表当前项 -->
<foreach collection="authors" open="(" close=")" separator="," item="author">
#{author}
</foreach>
</select>
@Test
public void getNewsByAuthor() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
INewsMapper mapper = sqlSession.getMapper(INewsMapper.class);
List<News> newsByAuthor = mapper.getNewsByAuthor(new String[]{"sport", "news"});
for(News item:newsByAuthor){
System.out.println(item.toString());
}
//update news where nid=170
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
8、查询列的引用
<sql id="news">
nid,ntitle,ncreateDate,ncontent
</sql>
<select id="getNews" resultType="News">
select <include refid="news"/> from news
<!--trim prefix代表添加一个前缀 prefixOverrides将and|or替换 只替换第一个-->
<trim prefix="where" prefixOverrides="AND|OR">
<!--ntitle不用加#{} 并且用and 或者用or-->
<if test="ntitle!=null and ntitle!=''">
and ntitle like concat('%',#{ntitle},'%')
</if>
<if test="ntid!=0">
and ntid=#{ntid}
</if>
</trim>
</select>
3、多对一
查询所有的新闻,包含新闻的分类
单条SQL:
mapper接口层:
//查询所有新闻数据
public List<News> getAllNews() throws Exception;
小配置文件:
<resultMap id="newsMap" type="News">
自动关联的情况下,只要保证实体属性和数据表字段一致即可
关联映射一的一方 property实体中关联一的一方的属性
<association property="topic" javaType="Topic">
<!–配置关联的属性,写实体与表不一致的字段–>
</association>
</resultMap>
<select id="getAllNews" resultMap="newsMap">
select * from news,topic where news.ntid=topic.tid
</select>
测试类:
/**
* 多对一
* @throws Exception
*/
@Test
public void getAllNews() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
INewsMapper mapper = sqlSession.getMapper(INewsMapper.class);
List<News> newsList = mapper.getAllNews();
for(News news:newsList){
System.out.println(news.toString());
}
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
多条SQL:
mapper接口层:
//查询所有新闻数据
public List<News> getAllNews() throws Exception;
小配置文件:
<resultMap id="newsMap" type="News">
<!--植入一的一方的对象-->
<association property="topic" javaType="Topic" select="getTopicByNtid" column="ntid"></association>
</resultMap>
<select id="getAllNews" resultMap="newsMap">
select * from news
</select>
<select id="getTopicByNtid" resultType="Topic">
select * from topic where tid=#{ntid}
</select>
测试类:
/**
* 多对一
* @throws Exception
*/
@Test
public void getAllNews() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
INewsMapper mapper = sqlSession.getMapper(INewsMapper.class);
List<News> newsList = mapper.getAllNews();
for(News news:newsList){
System.out.println(news.toString());
}
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
4、多对多
一对多成立,反之多对一也成立
一名教员可以教授多名学员,多名教员可以教授一名学员
Student
List<Teacher>
Teacher
List<Student>
解决方案:在实体当中互相植入多的一方的对象(如果利用到了第三章表,返回的结果是多条记录,那就不能采用单个对象接收数据,要采用集合)
mapper层接口
//查询微冷的雨教授的学员
public List<Teacher> getTeacherById(Integer tid) throws Exception;
mapper小配置文件
<resultMap id="teacherMap" type="Teacher">
<!--植入多的一方的对象-->
<collection property="stuList" ofType="Student">
</collection>
</resultMap>
<select id="getTeacherById" resultMap="teacherMap">
select * from teacher,student,t_s_relation where teacher.tid=t_s_relation.tid and student.stuid=t_s_relation.stuid
and teacher.tid=#{tid} and t_s_relation.tid=#{tid}
</select>
测试类:
@Test
public void getTeacherById() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
ITeacherMapper mapper = sqlSession.getMapper(ITeacherMapper.class);
List<Teacher> teacherList = mapper.getTeacherById(2);
for (Teacher teacher:teacherList){
System.out.println(teacher.toString());
}
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
5、自关联
在自身内部关联自身集合
实体类:
public class City {
private Integer cid; //地区编号
private String cname; //地区名称
private Integer parentid; //上一级分类ID
//自关联,就是在内部注入本身类型的集合
private List<City> cityList;
}
mapper层接口
//北京市下面的所有城区
public City getCity(Integer cid) throws Exception;
小配置文件
<resultMap id="cityMap" type="City">
<id property="cid" column="cid"/>
<collection property="cityList" ofType="City" select="getChildCity" column="cid">
</collection>
</resultMap>
<!--自关联怎么样去查询-->
<select id="getCity" resultMap="cityMap">
select * from city where cid=#{cid}
</select>
<select id="getChildCity" resultMap="cityMap">
select * from city where parentid=#{cid}
</select>
测试类:
@Test
public void getCity() throws Exception {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
ICityMapper mapper = sqlSession.getMapper(ICityMapper.class);
City city = mapper.getCity(1);
System.out.println(city.toString());
//关闭资源
MybatisUtil.closeSqlSession(sqlSession);
}
八、延迟加载
称为懒加载,可以控制加载的方式,主要用于关联查询数据上,可以减轻数据库服务器压力,Mybatis默认不开启延迟加载
延迟加载时机:
1、直接加载
在执行Select查询出主加载对象数据完毕后,直接执行关联对象的查询
2、侵入式延迟加载
先执行主加载对象的查询,不会立马执行关联对象的查询,当调用主加载对象的属性时才会执行关联查询
3、深度延迟
先执行主加载对象的查询,不会立马执行关联对象查询,当调用关联对象的属性才会执行关联查询
4、延迟加载配置过程
1.开启延迟加载:在大配置文件中添加LazyLodingEnabled属性值为true,默认为false代表不开启延迟加载,如果开启延迟加载不配置延迟加载方式,默认为深度
2.开启侵入式延迟加载:在大配置文件中,配置延迟加载方式,添加aggressiveLazyLoading属性,true代表的是侵入式延迟加载,false代表的是深度延迟加载
在3.4.1版本包括版本以前默认是ture代表侵入式延迟加载,在版本以后默认值为false代表深度延迟
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--配置侵入式延迟加载,如果不手动配置,默认为深度延迟
aggressiveLazyLoading 默认值为false代表默认执行深度延迟
-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
九、缓存
1、一级缓存
默认是开启一级缓存,所在的范围SqlSession范围,只要SqlSession没有关闭,那么缓存中的数据仍然有效
缓存说明:缓存是根据namespace进行划分的,不同的namespace缓存数据相互不干扰,缓存是为了减轻数据库服务器的压力,也就是说相同的查询只需要第一次执行数据库
2、一级缓存证明
统一个SqlSession范围内,无论创建多少个Mapper对象,只要第一次查询时将对应数据放到缓存中,后续再次读取该数据时执行得都是缓存
增删改会不会对以及缓存有影响:增删改会清空以及缓存数据,从新执行数据库查询,但是如果没有commit数据,请各位同学注意,可能会产生脏数据
3、一级缓存结论
1.Mybatis默认开启一级缓存,作用域为SqlSession,统一个SqlSession查询相同数据缓存生效
2.缓存查询依据:根据namespace+Sql的ID和Sql语句本身,如果发现都相同走缓存,不同走查询
3.增删改会清空一级缓存,但是不要忘了commit,否则会出现脏数据
3、二级缓存
需要对象实现序列化
需要手动开启,所在的范围与整个应用程序相同,与SqlSession无关
开启二级缓存方式:
1.大配置文件中开启二级缓存
<!--配置日志-->
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.小配置文件开启缓存
<cache/>
增删改对二级缓存影响:
如果commit会重新刷新二缓数据,执行数据库查询,如果没有commit则不刷新走缓存数据
关闭刷新缓存:默认情况下增删改只要提交,那么就会清空缓存,重新查询数据库 可以关闭刷新缓存,这样在提交时就不回重新刷新缓存数据,但是会出现脏数据
1.增删改可以关闭刷新缓存
flushCache用在增删改时,默认值为true代表刷新缓存,如果用在查询默认值为false代表读取缓存中数据
<!--flushCache代表刷新缓存,false为不刷新,也就是说在增删改提交数据后,还是会走缓存,不重新发送SQL-->
<insert id="addTeacher" flushCache="false">
insert into teacher(tname) values (#{tname})
</insert>
查询不应用缓存:
useCache用在查询时,默认值为true代表的应用缓存 false代表不应用缓存,每一次都发SQL
<select id="getAllTeacher" resultMap="teacherMap" useCache="false">
<!--select * from teacher,student,t_s_relation where teacher.tid=t_s_relation.tid and student.stuid=t_s_relation.stuid-->
select * from teacher
</select>
4、Mybatis整合第三方缓存Ehcache
需要开启二缓,依赖于二缓
1.导入依赖 ehcache mybatis-ehcache
2.配置二级缓存策略,使用Ehcache第三方缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.添加Ehcache配置文件:ehcache.xml
十、Mybatis分页插件PageHelper
内置属性只关注查询数据SQL,不关注分页SQL,自动封装数据,pageHelper中国人
配置步骤:
1、导入依赖
com.github.pageHelper
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.0</version>
</dependency>
2、配置插件
在mybatis大配置文件当中配置pagehelper插件
<!--mybaits中pagehelper分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper"></plugin>
</plugins>
3、创建Mapper方法,实现查询
//根据新闻分类,和新闻标题进行动态SQL查询
public List<News> getNewsByTitleAndTid(@Param("ntid") Integer ntid,@Param("ntitle") String ntitle) throws Exception;
4、创建小配置文件,不写limit
<resultMap id="newsMap" type="News">
<!--关联对象-->
<association property="topic" javaType="Topic"></association>
</resultMap>
<select id="getNewsByTitleAndTid" resultMap="newsMap">
select * from news,topic where news.ntid=topic.tid
<if test="ntid!=0">
and ntid=#{ntid}
</if>
<if test="ntitle!=null and ntitle!=''">
and ntitle like concat('%',#{ntitle},'%')
</if>
</select>
5、创建Service层
public PageInfo<News> getNewsByTitleAndTid(Integer tid, String ntitle, Integer pageNum, Integer pageSize) throws Exception;
6、创建ServiceImpl层,实现分页
public class INewsServiceImpl implements INewsService {
private INewsMapper iNewsMapper;
public SqlSession sqlSession;
public INewsServiceImpl(){
sqlSession = MybatisUtil.getSqlSession();
iNewsMapper = sqlSession.getMapper(INewsMapper.class);
}
@Override
public PageInfo<News> getNewsByTitleAndTid(Integer tid, String ntitle, Integer pageNum, Integer pageSize) throws Exception {
//pageHelper分页配置
//步骤一:告诉PageHelper要第几页的多少条数据
Page<News> page = PageHelper.startPage(pageNum, pageSize);
//步骤二:查询数据,注意:之分页紧跟PageHelper后面的一条SQL
List<News> newsList = iNewsMapper.getNewsByTitleAndTid(tid, ntitle);
return page.toPageInfo();
}
}
7、测试
@Test
public void pageHelper() throws Exception {
INewsServiceImpl service = new INewsServiceImpl();
PageInfo<News> pageInfo = service.getNewsByTitleAndTid(0, "", 2, 3);
System.out.println("===============================================================================");
List<News> list = pageInfo.getList();
for (News news : list) {
System.out.println(news.toString());
}
MybatisUtil.closeSqlSession(service.sqlSession);
}
8、PageHelper排序
调用startPage,直接传入排序的列,默认为升序 可以改为倒叙:列名 desc
public static <E> Page<E> startPage(int pageNum, int pageSize, String orderBy) {
Page<E> page = startPage(pageNum, pageSize);
page.setOrderBy(orderBy);
return page;
}
@Override
public PageInfo<News> getNewsByTitleAndTid(Integer tid, String ntitle, Integer pageNum, Integer pageSize) throws Exception {
//pageHelper分页配置
//步骤一:告诉PageHelper要第几页的多少条数据
Page<News> page = PageHelper.startPage(pageNum, pageSize,"ncreateDate desc");
//步骤二:查询数据,注意:之分页紧跟PageHelper后面的一条SQL
List<News> newsList = iNewsMapper.getNewsByTitleAndTid(tid, ntitle);
return page.toPageInfo();
}
十一、注解开发
1、 @SELECT的使用方式
在Mapper接口中方法上使用该注解,里面写SQL
//使用Select注解进行模糊查询
@Select("select * from news,topic where news.ntid=topic.tid and ntitle like concat('%',#{ntitle},'%')")
public List<News> getNewsByTitle(@Param("ntitle") String ntitle) throws Exception;
2、 关联查询
可以使用@ResultMap关联使用resultMap中id 或者也可以指向@Results中ID ,但是@ReusltMap和@Results不能同时出现在一个方法上
2.2.1 关联查询一的一方
//使用注解关联映射
@Results(id = "newsMapByMapper",value = {
//关联一的一方
@Result(property = "topic",column = "ntid",one = @One(select="com.wdksoft.mapper.INewsMapper.getTopic"))
})
@Select("select * from news where ntitle like concat('%',#{ntitle},'%')")
public List<News> getNewsByTitle(@Param("ntitle") String ntitle) throws Exception;
//查询一的一方的数据
@Select("select * from topic where tid=#{ntid}")
public Topic getTopic(Integer ntid) throws Exception;
2.2.2 关联查询多的一方
@Results(id = "topicResult",value = {
@Result(id=true,property = "tid",column = "tid"),
//关联多的一方
@Result(property = "newsList",column = "tid",many = @Many(select = "com.wdksoft.mapper.ITopicMapper.getNews"))
})
//查询新闻分类编号为4的新闻,包含新闻集合
@Select("select * from topic where tid=#{tid}")
public Topic getTopic(Integer tid) throws Exception;
@Select("select * from news where ntid=#{tid}")
public List<News> getNews(Integer tid) throws Exception;
3、 添加数据
//添加数据,添加数据返回主键@Options
@Insert("insert into topic(tname) values(#{tname})")
@Options(useGeneratedKeys = true,keyProperty = "tid")
public int insertTopic(Topic topic) throws Exception;
4、 修改数据
//修改数据
@Update("update topic set tname=#{tname} where tid=#{tid}")
public int updateTopic(Topic topic) throws Exception;
5、删除数据
//删除数据
@Delete("delete from topic where tid=#{tid}")
public int deleteTopic(Integer tid) throws Exception;
十二、MyBatis项目开发-超市账单管理系统
1.超市账单管理系统-登录功能
1.1 创建一个Maven的Web工程,将所需要的的页面素材添加到webapp下
1.2 导入依赖,Mybatis依赖以及JavaEE-api依赖
1.3 搭建分层,创建实体,在User实体当中植入角色对象
1.4 创建Mybatis大配置文件
1.5 创建Mapper接口层,内置一个登录的方法
//登录
public Smbms_User loginUser(@Param("userCode") String userCode, @Param("userPassword") String userPassword) throws Exception;
1.6 创建小配置文件ISmbmsUserMapper.xml文件
<resultMap id="userRoleMap" type="Smbms_User">
<association property="smbms_role" javaType="Smbms_Role"></association>
</resultMap>
<select id="loginUser" resultMap="userRoleMap">
select * from smbms_user where userCode=#{userCode} and userPassword=#{userPassword}
</select>
1.7 创建Service层接口,创建业务方法
//登录
public Smbms_User loginUser(String userCode,String userPassword) throws Exception;
1.8 创建Service层接口实现类,通过SqlSession调用Mapper接口对象
//植入Mapper层对象
private ISmbmsUserMapper iSmbmsUserMapper;
public ISmbmsUserServiceImpl(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
iSmbmsUserMapper=sqlSession.getMapper(ISmbmsUserMapper.class);
}
@Override
public Smbms_User loginUser(String userCode, String userPassword) throws Exception {
return iSmbmsUserMapper.loginUser(userCode,userPassword);
}
1.9 Servlet处理用户请求,调用Service层
@WebServlet("/ISmbmsUserServlet")
public class ISmbmsUserServlet extends HttpServlet {
//植入Service层对象
private ISmbmsUserService iSmbmsUserService=new ISmbmsUserServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action=req.getParameter("action");
if(action!=null){
//登录逻辑
if(action.equals("loginUser")){
//接收用户名和密码
String userCode=req.getParameter("userCode");
String userPassowrd=req.getParameter("userPassowrd");
try {
Smbms_User smbms_user = iSmbmsUserService.loginUser(userCode, userPassowrd);
if(smbms_user!=null){
//保存用户数据到Session
req.getSession().setAttribute("user",smbms_user);
//跳转页面
req.getRequestDispatcher("welcome.jsp").forward(req,resp);
}else{
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
2.超市账单管理系统-账单分页列表
2.0 导入PageHelper和FastJSON依赖,以及配置PageHelper插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper"></plugin>
</plugins>
2.1 搭建分层,实体,Bill账单植入一的一方供应商对象
2.2 创建Mapper接口,创建一个查询方法
//查询账单列表数据
public List<Smbms_Bill> getBill(@Param("productName") String productName, @Param("providerId") Integer providerId, @Param("isPayment") Integer isPayment) throws Exception;
2.3 创建Mapper小配置文件
<resultMap id="billProMap" type="Smbms_Bill">
<association property="smbmsProvider" javaType="Smbms_Provider"></association>
</resultMap>
<select id="getBill" resultMap="billProMap">
select * from smbms_bill,smbms_provider where smbms_bill.providerId=smbms_provider.pid
<if test="productName!=null and productName!=''">
and productName like concat('%',#{productName},'%')
</if>
<if test="providerId!=0">
and providerId=#{providerId}
</if>
<if test="isPayment!=0">
and isPayment=#{isPayment}
</if>
</select>
2.4 创建Service业务逻辑接口
//查询账单列表数据
public PageInfo<Smbms_Bill> getBill( String productName,Integer providerId,Integer isPayment,Integer pageNum,Integer pageSize) throws Exception;
2.5 创建Serivce业务逻辑接口实现类
public class ISmbmsBillServiceImpl implements ISmbmsBillService {
//植入Mapper层对象
private ISmbmsBillMapper iSmbmsBillMapper;
public ISmbmsBillServiceImpl(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
iSmbmsBillMapper=sqlSession.getMapper(ISmbmsBillMapper.class);
}
@Override
public PageInfo<Smbms_Bill> getBill(String productName, Integer providerId, Integer isPayment, Integer pageNum, Integer pageSize) throws Exception {
//开始分页
Page<Smbms_Bill> page = PageHelper.startPage(pageNum, pageSize);
//执行查询
List<Smbms_Bill> bill = iSmbmsBillMapper.getBill(productName, providerId, isPayment);
return page.toPageInfo();
}
}
2.6 Servlet处理页面请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action=req.getParameter("action");
if(action!=null){
if(action.equals("getBill")){
//获取条件
String productName=req.getParameter("productName");
String providerId=req.getParameter("providerId");
String isPayment=req.getParameter("isPayment");
String pageNum=req.getParameter("pageNum");
String pageSize=req.getParameter("pageSize");
try {
//查询数据
PageInfo<Smbms_Bill> pageInfo = iSmbmsBillService.getBill(productName, 0, 0, Integer.parseInt(pageNum), Integer.parseInt(pageSize));
String pageInfoStr = JSON.toJSONString(pageInfo);
resp.getWriter().write(pageInfoStr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2.7 billList.jsp页面发送Ajax请求
$(function () {
load(1);
});
function load(pageNum) {
$.ajax({
url:"ISmbmsBillServlet",
type:"POST",
data:{"action":"getBill","pageNum":pageNum,"pageSize":3},
dataType:"JSON",
success:function (pageInfo) {
alert(pageInfo);
}
})
}

浙公网安备 33010602011771号