Mybatis
Mybatis是支持普通sql查询,存储过程和高级映射的优秀持久层框架,消除了几乎所有的jdbc代码和参数的手工设置以及结果集的检索,mybatis使用简单的xml或者注解用户配置和原始映射,将接口和java的pojos(plan old java objects)映射成数据库的记录,
ORM:(Object Relations Mapper)
关系对象映射,是为了解决关系型数据库与简单java对象的(POJO)映射关系的一种技术,简单的说就是通过使用ORM扫描对象和数据库之间映射的元数据,将程序中的对象持久化到关系型数据库中
Mybatis是半自动映射工具,和全自动的区别:
Mybatis在查询关联对象,或者关联对象时,需要手动自己写sql语句,所以叫ORM的半自动工具,Hibernate属于全自动映射工具,使用hibrenate进行查询关联对象或者关联集合对象时,可以根据关系模型直接获取,所以叫全自动
传统JDBC开发存在的问题:
频繁的连接对象和释放,容易造成资源的浪费,影响系统性能,可以使用连接池来完成,但是jdbc 的连接池需要自己手动编写;sql语句定义、参数设置、结果集处理存在硬编码,很多项目中sql变化的可能性很大,一旦发生变化,需要修改sql语句,系统需要重新编译、重新发布,维护不容易;使用preparedStatement向占位符传参具有硬编码,因为where条件不一定,可能或多或少对其进行修改,系统不好维护,结果集处理可能存在重复代码,如果用java映射可能会方便很多。
JDBC编程的不足,mybatis如何解决
jdbc会频繁的操作数据库,对数据库频繁连接和额释放,及其消耗系统资源,影响系统的性能,当然使用连接池会解决这一个问题,但是jdbc的连接池要自己写,
mybatis会在xml里面对数据库的连接进行配置,直接映射即可使用
jdbc中的sql语句写在程序中,而现实的sql语句经常会改变,每当改变一次,都需要对程序重新编译,维护不易,
mybatis将sql语句定义在xxxMapper.xml文件中,与java代码分离,
sql语句传参不方便,而where条件不一定,可能多或者少,占位符和条件要一一对应。
mybatis自动将sql语句映射到Java中
对结果集解析麻烦,sql变化导致解析变化,且解析前需要遍历,如果将数据库封装到POJO中会比较方便,
mybatis自动将sql语句映射到java对象
Mybatis优缺点
优点:基于SQL语句编程,相当灵活,不会对数据库的设计和程序造成影响,sql语句写在xml中,实现了sql语句和java的解耦,便于统一管理,提供xml标签,支持编写动态sql,并复用,和jdbc相比消除了大量的代码冗余,并减少了很多代码量,很好的与各种数据库兼容,提供映射标签,支持对象与数据库的的ORM字段的映射关系,提供对象关系映射标签,支持对象关系主键维护,能够和spring集成。适用于专注于SQL本身,是一个足够灵活的DAO层解决方案,对性能要求高,或者需求变化大的项目,比如互联网的项目,是一个不错的选择。
缺点:sql编写量较大,尤其是字段多、关联表多的时候,对开发人员对SQL编写的能力有一定要求,sql依赖数据库,可移植性差,不能随意更换数据库
第一个Mybatis
配置Mybatis
<?xml version="1.0" encoding="UTF-8" ?>
数据源文件jdbc.properites
driver = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/200701?characterEncoding=utf8
username=root
password=123456
数据库设计
CREATE DATABASE mybatis
;
USE mybatis
;
CREATE TABLE user
(
id
INT(20) NOT NULL PRIMARY KEY,
name
VARCHAR(30) DEFAULT NULL,
pwd
VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
insert into user
(id,name,pwd) values(1,‘张三’,‘123’),(2,‘tt’,‘123456’),(3,‘yy’,‘789’);
mybatis工具类
package com.ty.tao.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
//获取sqlSessionFactory -->SqlSession
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
private static SqlSession sqlSession;
static {
try {
//使用mybatis获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
sqlSessionFactory = builder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
实体类
package com.ty.tao.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Dao接口
package com.ty.tao.dao;
import com.ty.tao.pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
}
接口实现类:变成现在的一个xml配置文件
<?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接口-->
<mapper namespace="com.ty.tao.dao.UserDao">
<!--cache缓存--> <!--select id指方法的名字 resultType一个结果 resultMap 结果集-->
<select id="getUserList" resultType="com.ty.tao.pojo.User">
select * from user
</select>
</mapper>
测试
使用JUnit测试,
@Test
public void test(){
//1获取sqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
//执行
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
增删改查
接口编写
//增加一个用户
int addUser(User user);
//万能map集合
int addUser2(Map<String, Object> map);
//更新一个用户
int updateUser(User user);
int updateUser2(Map<String,Object> map);
//删除用户
int deleteUser(int id);
xml编写
<!--对象中的属性可以直接取出-->
<insert id="addUser" parameterType="com.ty.tao.pojo.User">
insert into user(id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
<!--使用Map集合 userId map的key-->
<insert id="addUser2" parameterType="map">
insert into user(id,name,pwd) values(#{userId},#{username},#{password})
</insert>
<update id="updateUser" parameterType="com.ty.tao.pojo.User">
update user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
<!--只修改其中的某一个值,可以通过map集合,传入要修改的值和id即可-->
<update id="updateUser2" parameterType="map">
update user set name=#{name} where id=#{id};
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
JUint测试
//增删改需要提交事务
@Test
public void addUserTest(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
User user = new User(1,"ee","132");
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.addUser(user);
sqlSession.commit();
System.out.println(user);
System.out.println(i);
sqlSession.close();
}
@Test
public void updateUserTest(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int qq = mapper.updateUser(new User(4, "qq", "789789789"));
System.out.println(qq);
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUserTest(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.deleteUser(1);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
//测试map 增加
@Test
public void addUser2Test(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map = new HashMap<>();
map.put("userId",1);
map.put("username","hello");
map.put("password","123456");
int i = mapper.addUser2(map);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser2Test(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map = new ConcurrentHashMap<>();
map.put("id",2);
map.put("name","eee");
int i = mapper.updateUser2(map);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
Map
只修改其中的某一个值,可以通过map集合,传入要修改的值和id即可
<update id="updateUser2" parameterType="map">
update user set name=#{name} where id=#{id};
</update>
使用Map集合 userId map的key
<resultMap id="userListMap" type="com.ty.tao.pojo.User">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="password" column="pwd" />
</resultMap>
<insert id="addUser2" parameterType="map">
insert into user(id,name,pwd) values(#{userId},#{username},#{password})
</insert>
模糊查询
java代码执行的时候,传递通配符% %
select * from mybatis.user where name like "%" #{value} "%"
sql中拼接
map传递参数,直接再sql中取出key值即可 [parameterType=map]
对象传递参数,直接再sql对象中取对象的属性即可 [parameterType=object]
只有一个基本类型的情况下,直接再sql中取到
多个参数用map或者注解
假设实体类和数据库中参数过多,应该考虑使用map,
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
两种方式:
1.使用typeAliases标签定义自子标签指定类名,并 确认新的别名
<typeAliases >
<package name="com.ty.tao.pojo"/>
</typeAliases>
2.使用package 默认类名位包内的类名,首字母小写
3.再实体类少的时候,使用第一种,多的时候使用第二种,但是第一种可以取别名,第二种没有,如果非要改,需要再实体类内加入注解Alises
日志
日志分类
SLF4J
LOG4J 掌握
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING 掌握
NO_LOGGING
mybatis具体使用哪个,在设置中设置
使用STDOUT_LOGGING日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
使用LoG4J日志工厂
什么是log4J
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1.导包
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
2.编写配置文件 log4j.properites
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#日志格式
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
#输出文件地址
log4j.appender.file.File=./log/ty.log
#输出文件大小,如果超过10m生成一个新的
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别 都是debug才输出,其他不输出输出对象mybatis sql Statement ResultSetPreparedStatement
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sq1.PreparedStatement=DEBUG
配置log4j为日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
log4j的使用,直接测试运行,
分页
<select id="getUserListLimit" resultMap="userListMap" parameterType="map" resultType="com.ty.tao.pojo.User">
select id,name,pwd from mybatis.user limit #{startIndex},#{pageSize}
</select>
测试
@Test
public void getUserListLimitTest(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String,Integer> map = new ConcurrentHashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userListLimit = mapper.getUserListLimit(map);
for (User user : userListLimit) {
System.out.println(user);
}
System.out.println(userListLimit);
sqlSession.close();
}
简单使用
1.在要使用的Log4j的地方 导入Logger 是import org.apache.log4j.Logger下面的
2.日志对象,当前类作为参数对象class
static Logger logger = Logger.getLogger(UserDaoTest.class);
使用注解,不需要编写xml文件
使用注解完成CRUD
1.在工具类中编写自动提交事务
public static SqlSession getSqlSession(){
sqlSession = sqlSessionFactory.openSession(true);
return sqlSession;
}
2.编写接口,使用注解的方式,
//使用注解的方式
@Select("select id,name,pwd password from user")
List<User> getUsers();
//方法内存在多个参数时,需要在参数前面加入@Pram
@Select("select * from user where id=#{id}")
List<User> getOneUserById(@Param("id") int id);
//注解形式增加
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
int addOneUser(User user);
//更新
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int UpdateOneUser(User user);
//删除
@Delete("delete from user where id=#{uid}")
int deleteOneUser(@Param("uid") int id);
测试类:
将接口注册绑定到核心配置文件中
<mappers>
<mapper class="com.ty.tao.dao.UserDao" />
</mappers>
Lombok
1.下载插件
浏览器下载
2.导入jar包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
3.在实体类中加注解
@Alias("hello")
@Data //定义了get、set、toString、hashcode、equals
@AllArgsConstructor //定义有参的
@NoArgsConstructor //定义无参的
public class User {
private int id;
private String name;
private String password;
}
Data最常用:生成get、set、toString、hashcode、equals、无参构造
@Data //定义了get、set、toString、hashcode、equals
@AllArgsConstructor //定义有参的
@NoArgsConstructor //定义无参的
多对一,一对多
create table teacher
(
id
int(10) not null,
name
varchar(20) DEFAULT NULL,
PRIMARY KEY(id
)
)ENGINE=INNODB DEFAULT charset=utf8;
INSERT into teacher(id
,name
) values(1,‘涛老师’)
create table student
(
id
int(10) not null,
name
varchar(30) DEFAULT null,
tid
int(10) DEFAULT null,
PRIMARY key (id
),
key fktid
(tid
),
CONSTRAINT fktid
FOREIGN key (tid
) REFERENCES teacher
(id
)
)ENGINE=INNODB DEFAULT charset=utf8;
INSERT into student
(id
,name
,tid
) values(1,‘小红’,‘1’);
INSERT into student
(id
,name
,tid
) values(2,‘小黄’,‘1’);
INSERT into student
(id
,name
,tid
) values(3,‘小蓝’,‘1’);
INSERT into student
(id
,name
,tid
) values(4,‘小绿’,‘1’);
INSERT into student
(id
,name
,tid
) values(5,‘小青’,‘1’);
INSERT into student
(id
,name
,tid
) values(6,‘小紫’,‘1’);
按照查询嵌套处理 子查询
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id" />
<result property="name" column="name" />
<!--复杂属性单独处理 对象:association 集合:connection-->
<association property="teacher" javaType="Teacher" column="tid" select="getTeacher" />
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
方式2
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid= t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result column="id" property="sid" />
<result column="name" property="sname" />
<association property="teacher" javaType="Teacher" >
<result property="tname" column="name" />
</association>
</resultMap>
动态sql
什么是动态sql
指:根据不同的条件产生不同的sql语句
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少
CREATE TABLE blog
(
id
VARCHAR(50) NOT NULL COMMENT ‘博客id’,
title
VARCHAR(100) NOT NULL COMMENT ‘博客标题’,
author
VARCHAR(30) NOT NULL COMMENT ‘博客作者’,
create_time
datetime NOT NULL COMMENT ‘创建时间’,
views
INT(30) NOT NULL COMMENT ‘浏览量’
)ENGINE=INNODB DEFAULT charset=utf8
动态sql查询
select * from blog
title = #{title}
and author = #{author}
and views = #{views}
<select id="queryForeachBlog" parameterType="blog" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
缓存
查询:连接数据库:耗资源
一次查询的结果可以存放在在一个直接可以取到的地方 – >内存 : 缓存
在此查询相同数据库的适合,直接到内存里面就行了
概念:Cache
内存中的临时数据
将用户经常查询的数据防止在缓存(内存)中,用户再去查询数据的时候,不在从数据库中查询,而是从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
为什么使用缓存:建设和数据库的交互次数,减少系统开销,提高系统效率
什么样的数据能使用缓存:经常查询并且不经常改变的数据(可以使用缓存)----- 查询
不经常查询并且经常改变的数据 不使用缓存 ----写
二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存,
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
工作机制:
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话被关闭了,这个会话对应的一级缓存就没有了,但是想要的是当会话关闭的时候,一级缓存中的数据被保存到而二级缓存 中,
新的会话查询信息,就可以从二级缓存中获取,
不同的mapper查询出的数据反正该自己对应的缓存map中
步骤;
1.开启全局缓存
显示的开启二级缓存
2.在要使用二级缓存中mapper开启
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>