文章目录
引入
1.jdbc, Dbutils(工具类), jdbcTemplate(spring的)
sql语句编写在java代码里,硬编码高耦合
2.Hibernate:全自动ORM(Object Relation Mapping)框架旨在消除sql
编写sql,预编译,设置参数,执行sql,封装结果处于黑箱
黑箱操作,无法自定义编写sql,希望可以将sql交给开发人员
3.引出mybatis(半自动)
sql语句编写在配置文件
预编译,设置参数,执行sql,封装结果处于黑箱
sql与java编码分离,sql是开发人员控制
配置
导入依赖
<!--MyBatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
项目打包编译是打包项目包中的.class文件和resourses中的xml文件,所以一般在resourses中放xml文件
所以希望xml文件在项目包的话,要加以下依赖
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
主配置文件
包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器
<?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>
<!--environments配置环境组-->
<!--default默认环境-->
<environments default="development">
<!--environment单个环境-->
<environment id="development">
<!--transactionManager配置事务管理器-->
<transactionManager type="JDBC"/>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://rm-bp1u4664aff1hip7plo.mysql.rds.aliyuncs.com:3306/practice?"/>
<property name="username" value="root"/>
<property name="password" value="Aa123456"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.xml需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="StudentDao.xml"/>
</mappers>
</configuration>
工具类
将固定步骤放入此类,去除了冗余的操作
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。 SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用mybatis第一步、获取sqlSessionFactory对象
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们就可以从中获得 SqlSession 的实例了。
// SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
例子
1.创建student表
CREATE TABLE `student`
(
id INT NOT NULL,
`name` VARCHAR NULL,
email VARCHAR NULL,
age INT NULL,
PRIMARY KEY(id)
)
CHARACTER SET utf8 COLLATE utf8_bin ENGINE INNODB;
2.创建实体类
属性名和列名保持一致(也可以取别名)
package practice01.domain;
public class Student {
private int id;
private String name;
private String email;
private int age;
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
3.创建Dao层,定义操作数据库方法
创建个dao包,创建接口和方法
public interface StudentDao {
Student selectStudentById(int id);
}
4.创建xml文件写sql语句
在resources里创建个dao包,里面写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" >
<mapper namespace="practice01.dao.StudentDao">
<!--
id是自定义标识,推荐使用接口方法
resultType告诉mybatis执行sql语句后将数据类型赋值的对象类型
-->
<select id="StudentDao" resultType="practice01.domain.Student">
select * from student where id=#{id}
</select>
</mapper>
id是自定义标识,推荐使用接口方法
resultType告诉mybatis执行sql语句后将数据类型赋值的对象类的全名(底层通过反射创建对象)
namespaces=接口的全类名(eclipse)[namespaces=接口名(idea)]
id =接口中的方法名
parameterType=方法中的参数类型
SQL语句中的参数=方法中的参数名
属性名和列名保持一致(也可以取别名)
5.在mybatis的主配置文件注册
<mappers>
<mapper resource="StudentDao.xml"/>
</mappers>
6.测试
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
SqlSession sqlSession = MyBatisUtils.getSqlSession();
Student student = sqlsession.selectOne("selectStudentById",1);
selectOne
selectList
selectMap
insert delete update
另一种方法,此方法直接调用接口的方法和参数,比较明显好用
public class Mytest {
@Autowired
private StudentDao studentDao;
@Test
void test()
{
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//创建接口的代理对象
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.selectStudentById(1);
System.out.println(student);
sqlSession.close()
}
}
直接映射到在命名空间中同名的映射器类,并将已映射的 select 语句匹配到对应名称、参数和返回类型的方法。因此你就可以像上面那样,不费吹灰之力地在对应的映射器接口调用方法
黑色加粗字是底层的实现
底层原理
主配置文件:
数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器。读取XML配置文件后会将内容放在一个Configuration类中,Configuration类会存在整个Mybatis生命周期,以便重复读取。
SqlSessionFactory:
创建SqlSession实例,是单例的。因为每次访问数据库都需要一个SqlSession,所以SqlSessionFactiory的生命周期是贯穿整个Mybatis生命周期的,SqlSessionFactiory采用单例的原因是减少数据库连接资源
SqlSession:
SqlSession是一个会话,类似于jdbc的connection,它的生命周期是在请求数据库处理事务过程中,他是一个线程不安全的对象。多线程操作时因多注意他的隔离级别和数据库锁等。SqlSession使用需及时关闭
两种使用:
1.获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。
映射底层是动态代理,将执行接口方法通过反射抽象成method,在invoke函数先判断,如果是类的方法的话直接执行。如果不是类的话,将method传给MapperMethod对象。MapperMethod.execute()底层会调用sqlSwssion执行sql语句。这里动态代理相当于给了实现接口抽象方法的方法体
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
2.直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的,SqlSession通过Update、Select、Insert、Delete等方法操作
基于JDBC的改进,思路没变
resultMap和resultType
MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap.
resultType表示返回的数据类型,其实类属性和表字段底层已经映射过了。属性名和表的字段名一致,根据名字映射上。
resultMap适合比较复杂的查询,类属性和表字段名字不一致时,用resultMap提前映射好。
java类中定义的名字如图所示
可以看到他们不一样,需要有一个映射关系。这样避免过多的对代码进行修改。
需要在Mapper.xml中对数据进行映射,就可以用到resultmap
-->
<resultMap id="userMap" type="com.itheima.domain.User">
<id property="userId" column="id"></id>
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
resultType就需要替换成resultMap
<select id="findAll" resultMap="userMap">
select * from user
</select>
<insert id="saveUser" parameterType="com.itheima.domain.User">insert into USER (username,address,sex,birthday) values (#{username},#{address},#{sex},#{birthday});
</insert>
<update id="updateUser" parameterType="com.itheima.domain.User">
UPDATE user set username=#{username},address=#{address},sex=#{sex} where id=#{id}
</update>
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
<select id="findUserById" resultMap="userMap">
select * from user where id=#{id}
</select>
<select id="findUserByName" resultMap="userMap">
select * from user where username like #{username}
</select>
#{}和${}
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理s{}时,就是把s{}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
CRUD
sql语句是mysql的内容
package com.newer.dao;
import com.newer.pojo.User;
import java.util.List;
public interface UserMapper {
//查询全部用户
List<User> getUserList();
//根据ID查询用户
User getUserById(int id);
//insert一个用户
int addUser(User user);
//修改用户
int updateUser(User user);
//删除用户
int deleteUser(int id);
}
<?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绑定一个对应的Mapper接口-->
<mapper namespace="com.newer.dao.UserMapper">
<select id="getUserList" resultType="com.newer.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="com.newer.pojo.User">
select * from mybatis.user where id=#{id}
</select>
<insert id="addUser" parameterType="com.newer.pojo.User">
insert into mybatis.user (id,name,pwd) values (#{id},#{name },#{pwd})
</insert>
<update id="updateUser" parameterType="com.newer.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
</mapper>
多对一查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.newer.dao.StudentMapper">
<!--按照结果嵌套处理-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<association property="teacher" javaType="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
</association>
</resultMap>
</mapper>
一对多查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.newer.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid ,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<collection property="students" ofType="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tid" column="tid"></result>
</collection>
</resultMap>
mysql多对一查询
-
子查询
-
联表查询
一对多和多对一都是多表查询,如一个老师对应多学生,多学生对应一个老师。只是返回的对象不同。
模糊查询
string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”> select * from foo where bar like #{value} </select>
分页查询
limit
select * from user limit startIndex,pageSize
//分页
List<User> getUserByLimit(Map<String,Integer> map);
@Test
public void getUserByLimit(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
RowBounds分页
不通过sql语句实现分页
List<User> getUserByRowBounds();
<!--分页2-->
<select id="getUserByRowBounds" resultMap="UserMap">
select * from mybatis.user
</select>
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(1, 2);
//通过java代码层面实现分页
List<User> userList = sqlSession.selectList("com.rui.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
parameterType
@Param是告诉sql参数名称,而parameterType是告诉sql参数类型。
方法参数的类型是基本数据类型的话没必要写。sql语句#{}中填写参数名即可。
方法参数的类型是类的话,parameterType后填写对应的类,#{}中填写
方法参数的类型是类也可以给类取别名,然后#{}中类别名.成员变量名
我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map
Map
int addUser2(Map<String,Object> map);
<!--对象中的属性,可以直接取出来 parameterType=传递map中的key-->
<insert id="addUser2" parameterType="map">
insert into mybatis.user (id, name, pwd) values (#{userId},#{userName},#{password});
</insert>
@Test
public void addUser2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<>();
map.put(“userId”,4);
map.put(“userName”,“王五”);
map.put(“password”,“23333”);
mapper.addUser2(map);
//提交事务
sqlSession.commit();
sqlSession.close();
}
map中的key作为sql语句的参数
动态Sql
传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误。Mybatis的动态SQL功能正是为了解决这种问题, 其通过 if, choose, when, otherwise, trim, where, set, foreach标签,可组合成非常灵活的SQL语句,从而提高开发人员的效率。下面就去感受Mybatis动态SQL的魅力吧。
if
<select id="findUserById" resultType="user">
select * from user where
<if test="id != null">
id=#{id}
</if>
and deleteFlag=0;
</select>
上面例子: 如果传入的id 不为空, 那么才会SQL才拼接id = #{id}。 这个相信大家看一样就能明白,不多说。细心的人会发现一个问题:“你这不对啊! 要是你传入的id为null, 那么你这最终的SQL语句不就成了 select * from user where and deleteFlag=0, 这语句有问题!”
where
<select id="findUserById" resultType="user">
select * from user
<where>
<if test="id != null">
id=#{id}
</if>
and deleteFlag=0;
</where>
</select>
set
<update id="updateUser" parameterType="com.dy.entity.User">
update user
<set>
<if test="name != null">name = #{name},</if>
<if test="password != null">password = #{password},</if>
<if test="age != null">age = #{age}</if>
</set>
<where>
<if test="id != null">
id = #{id}
</if>
and deleteFlag = 0;
</where>
</update>
choose
Java中有switch, mybatis有choose
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
当title和author都不为null的时候, 那么选择二选一(前者优先), 如果都为null, 那么就选择 otherwise中的, 如果tilte和author只有一个不为null, 那么就选择不为null的那个
注解
@Param
@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中
public int getUsersDetail(@Param("userid") int userid);
<select id="getUserDetail" statementType="CALLABLE" resultMap="baseMap">
Exec WebApi_Get_CustomerList #{userid}
</select>
void mysave(@Param("pac123") Pac pac);
<insert id="mysave" parameterType="com.example.xmlsax_reader.entity.Pac">
insert into pac(productlevel,satelliteid) values (#{pac123.productLevel},#{pac123.satelliteID})//#{参数名.成员变量名}
</insert>