MyBatis笔记
介绍:
MyBatis可以简化JDBC操作,实现数据的持久化。
ORM:Object Relational Mapping(概念)
Mybatis是ORM的一个实现
映射:
orm可以使开发人员像操作对象一样操作数据库表
使用方式一. 基础方式:
1. 官网下包
2. 编写头文件
<?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="development">
<!-- default与下方ID一致,可以指定具体哪个配置-->
<environment id="development">
<!--事物的提交方式(一般是JDBC)
JDBC,利用JDBC方式处理方式(commit rollback close)
MANAGED,将事务交由其他组件托管(spring jobss)默认关闭连接,
↑ 取消关闭:<property name="closeConnection" value="false"/>
-->
<!--transactionManager末尾有个结束符号 -->
<transactionManager type="JDBC"/>
<!--数据库类型:
POOLED,传统JDBC(每次访问访问数据库,均要打开关闭数据库,此操作太费性能)
UNPOOLED,数据库连接池(三方)
JNDI,从tomcat中获取内置的数据库连接池(数据源)
-->
<dataSource type="POOLED">
<!-- 配置数据库信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载映射文件-->
<mapper resource="personMapper.xml"/>
</mappers>
</configuration>
**personMapper.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 : 该mapper.xml映射文件的 唯一标识 -->
<mapper namespace="personMapper" >
<!-- 通过namespace+id定位sql语句 -->
<!-- parameterType: 输入参数类型
resultType: 查询返回结果值得类型-->
<select id="queryPersonById" resultType="edu.mybaits.Person" parameterType="int">
select * from person where id = #{id}
</select>
<!-- 输入、输出参数只能一个,但是可以放集合,实现多输入 -->
<insert id="addPerson" parameterType="edu.mybaits.Person">
insert into person(id,name,age) values(#{id},#{name},#{age})
</insert>
<update id="updatePerson" parameterType="edu.mybaits.Person">
update person set name = #{name} , age = #{age} where id = #{id}
</update>
<delete id="deletePerson" parameterType="int">
delete from person where id = #{id}
</delete>
<!-- 如果返回值类型如果是个对象无论是单个还是多个,这里都是person不用list -->
<select id="queryAllPerson" resultType="edu.mybaits.Person">
select * from person
</select>
</mapper>
- 我用的是idea,两个文件都要在pom.xml中声明,否则提示找不到文件
<build>
<resources>
<resource>
<directory>src</directory>
<includes>
<include>conf.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src</directory>
<includes>
<include>personMapper.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3. 测试文件
/**
* @Author: qsX
* @Time: 2020-09-20 15:25
* @Description:
*/
public class TestMyBatis {
public static void main(String[] args) throws IOException {
/* Person person = queryPersonById(1);
System.out.println(person);*/
/* List<Person> list = queryAllPerson();
System.out.println(list);*/
Person person = new Person(3,"ww",55);
int count = updatePerson(person);
System.out.println(count);
}
/**
* @Description 增加人
* @Param [person]
* @Return void
*/
public static int addPerson (Person person) throws IOException {
SqlSession session = se();
String statement = "personMapper." + "addPerson";
//增加几名学生
int count = session.insert(statement,person);
//提交事务
session.commit();
session.close();
return count;
}
/**
* @Description 删除指定学生
* @Param [id]
* @Return int
*/
public static int deletePerson (int id) throws IOException {
SqlSession session = se();
String statement = "personMapper." + "deletePerson";
int count = session.delete(statement,id);
session.commit();
session.close();
return count;
}
/**
* @Description 修改指定学生信息
* @Param [id]
* @Return int
*/
public static int updatePerson (Person person) throws IOException {
SqlSession session = se();
String statement = "personMapper." + "updatePerson";
int count = session.update(statement,person);
session.commit();
session.close();
return count;
}
/**
* @Description 按学号查询人
* @Param [id]
* @Return edu.mybaits.Person
*/
public static Person queryPersonById (int id) throws IOException {
SqlSession session =se();
String statement = "personMapper." + "queryPersonById";
Person person = session.selectOne(statement,id);
session.close();
return person;
}
/**
* @Description 查询所有人
* @Param [id]
* @Return edu.mybaits.Person
*/
public static List<Person> queryAllPerson () throws IOException {
SqlSession session =se();
String statement = "personMapper." + "queryAllPerson";
List<Person> list = session.selectList(statement);
session.close();
return list;
}
/**
* @Description 生成Sqlsession
* @Param []
* @Return org.apache.ibatis.session.SqlSession
*/
public static SqlSession se () throws IOException {
//加载MyBabits配置文件(为了访问数据库)
Reader reader = Resources.getResourceAsReader("conf.xml");
//sqlSessionFactory - connection
//可以配置build的第二参数指定数据库环境(reader,"xxxxx")
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
return session;
}
}
使用方式二. mapper的动态代理方式crud、Mybatis接口开发(推荐使用):
约定优于配置
-
硬编码方式:abc.java -
Configuration conf = new Configuration();
conf.setName("myProject");
-
配置方式:abc.xml -
<name>myProject</name>
-
约定:默认值就是myProject
1. 基础环境
mybatis. jar/ojdbc. jar、conf. xml、mapper. xml
2. 不同之处
约定的目标: 省略掉statement,即根据约定直接可以定位出SQL语句
* 1. 方法名 和mapper.xml文件中的标签ID一致
* 2.方法中的 输入参数 和mapper.xml中标签的parameterType类型一致
* 3.方法中的 返回值 和mapper.xml文件中标签的resultType类型一致
* 除了以上约定,要实现接口中的方法 和Mapper. xml中SQL标签一一对应,还需要:
* 1.namespace的值 就是接口的全类名
接口👇(接口名要和Mapper.xml的namespace一致)
package mybatis;
/**
* @Author: qsX
* @Time: 2020-09-23 16:10
* @Description: 操作Mybatis的接口
*/
public interface personMapper {
Person queryPersonById(int id);
void addPerson(Person person);
}
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
personMapper personMapper = session.getMapper(mybatis.personMapper.class);
Person person = personMapper.queryPersonById(1);
3. 易错点
- Mapper.xml时通过接口的全类名找到的, 所以namespace值必须和接口名一致(用 . 连接包名)
- xml文件要放置resources文件中,
- 还有一个最傻的,记得开服务。。。
三、优化代码
1. 将数据库信息提出来
- 创建db.properties文件将配置信息写入
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/test
username = root
password = 123456
- 在conf.xml的
<configuration>
中用子标签<properties resource="db.properties"/>
引用调用 - 优化配置信息,动态引入
2. 全局参数
- 可以在conf.xml中配置
2. 别名(忽略大小写)
-
可以在conf.xml中配置
-
默认的自带内置常见类别名
三、自带处理器(类型转换器)
1. 系统的
2. 自定义
-
java代码 👉 JDBC类型
示例: 实体类Student 男:true 女:false 👉 数据库表Student 男:1 女:0
a. 创建转换器:需要实现TypeHandler接口
- 此接口有一个实现类是BaseTypeHandler,因此要实现转换器两种选择(继承实现类,接口都行)
- set : java 👉 sql , get:sql 👉 java
注意:jdbcType = “INTEGER” (integer需要是大写)
转换器👇
public class BooleanToIntConterver extends BaseTypeHandler<Boolean> {
/*java(boolean) → SQL(number)
* ps:PreparedStatement对象
* parameter:java
* jdbcType:jdbc操作的数据库类型
* */
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType) throws SQLException {
if (parameter) {
ps.setInt(i,1);
} else {
ps.setInt(i,0);
}
}
@Override
public Boolean getNullableResult(ResultSet rs, String columnName) throws SQLException {
int sexNum = rs.getInt(columnName); //rs.getInt(name)
return sexNum == 1 ? true : false;
}
@Override
public Boolean getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int sexNum = rs.getInt(columnIndex);//rs.getInt(1)
return sexNum == 1 ? true : false;
}
@Override
public Boolean getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int sexNum = cs.getInt(columnIndex);
return sexNum == 1 ? true : false;
}
}
b. 使用转换器👇
<!--使用了转换器
1.如果类中的属性和表中的字段能合理识别,则可以使用resultType,否则(boolean→number)使用resultMap
2.如果类中的属性和表中的字段能合理识别,则可以使用resultType,否则(id→no)使用resultMap
-->
<select id="queryPersonByIdWithConverter" resultType="mybatis.Person" parameterType="int" resultMap="PersonResult">
select * from person where id = #{id}
</select>
<resultMap id="PersonResult" type="mybatis.Person">
<!--分主键:id,非主键:result-->
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex" javaType="boolean" jdbcType="INTEGER"/>
</resultMap>
<!-- 使用了转换器 sql语句严格区分大小写#{name} -->
<insert id="addPersonWithConverter" parameterType="mybatis.Person">
insert into person(id,name,sex) values(#{id},#{name},#{sex,javaType=boolean,jdbcType=INTEGER})
</insert>
c. 新建接口类👇
Person queryPersonByIdWithConverter(int id);
d. 使用接口类👇
/**
* @Description 按学号查询人(有转换器)
* @Param [id]
* @Return mybaits.Person
*/
public static Person queryPersonByIdWithConverter (int id) throws IOException {
SqlSession session =se();
personMapper personMapper = session.getMapper(mybatis.personMapper.class);
Person person = personMapper.queryPersonByIdWithConverter(id);
session.close();
return person;
}
注意:
sql语句严格区分大小写#{name}
四、输入参数:parameterType
1.简单类型:(8个基本类型 + String)
#{}和${}的区别
#{任意值}
、${value}
,其中的标识符只能是value#{}
自动给 String类型 加引号‘ ’
,
${value}
原样输出,不会加单引号(可以手动加'${value}'
)一般用于动态排序#{}
防止SQL注入 ,${value}
不行
2.对象类型:
#{属性名}
${属性名}
3. 嵌套查询:
- mapper.xml 👇
<!-- 嵌套查询 -->
<select id="queryPersonByaddress" resultType="mybatis.Person" parameterType="mybatis.Person" >
select * from person where homeaddress like '%${address.HomeAddress}%' or schooladdress like '%${address.SchoolAddress}%'
</select>
- 接口 👇
List<Person> queryPersonByaddress(Person person);
- 实现 👇
Address address = new Address("1","1");
Person person = new Person();
person.setAddress(address);
List<Person> list = queryPersonByaddress(person);
System.out.println(list);
4. HashMap:
用MAP中的KEY值匹配占位符#{value},配得上就value替换占位符
- mapper.xml 👇
<!-- HashMap条件查询 -->
<select id="queryPersonBynameOrBySexWithHashMap" resultType="mybatis.Person" parameterType="HashMap" >
select * from person where name = #{name} or sex = #{sex}
</select>
- 接口 👇
List<Person> queryPersonBynameOrBySexWithHashMap(Map<String,Object> map);
- 实现 👇
Map<String,Object> PersonMap = new HashMap<>();
PersonMap.put("name","zs");
PersonMap.put("sex",true);
List<Person> list = queryPersonBynameOrBySexWithHashMap(PersonMap);
System.out.println(list);
五、Mybatis如何调用存储过程,(示例)
1. 查询某个性别的人总数
- 在数据库中创建存储过程👇
CREATE PROCEDURE queryCountBySexWithProcedure ( IN q_sex INT, OUT count INT ) BEGIN
SELECT
count( 1 ) INTO count
FROM
person
WHERE
sex = q_sex;
END
SHOW PROCEDURE STATUS;
👉 用于查看数据库中已有的存储过程
- mapper.xml 👇(数据库语句尽量不要加空格与换行)
<!-- 调用存储过程实现查询 callable 输入参数需要用HashMap -->
<select id="queryCountBySexWithProcedure" statementType="CALLABLE" parameterType="HashMap">
{CALL queryCountBySexWithProcedure
(#{q_sex,jdbcType=INTEGER,mode=IN},#{count,jdbcType=INTEGER,mode=OUT})}
</select>
- 接口 👇
void queryCountBySexWithProcedure(Map<String,Object> map);
- 实现 👇
Map<String,Object> Map = new HashMap<>();
Map.put("q_sex",true);
queryCountBySexWithProcedure(Map);
Object count = Map.get("count");
System.out.println(count);
2. 删除
- 在数据库中创建存储过程👇
CREATE PROCEDURE delectPersonByIdWithProcedure ( IN id INT ) BEGIN
DELETE
FROM
person
WHERE
id = id;
END
- mapper.xml 👇(数据库语句尽量不要加空格与换行)
<!-- 调用存储过程实现删除 callable -->
<select id="delectPersonByIdWithProcedure" statementType="CALLABLE" parameterType="HashMap">
{CALL delectPersonByIdWithProcedure(#{pid,jdbcType=INTEGER,mode=IN})}
</select>
- 接口 👇
void delectPersonByIdWithProcedure(Map<String,Object> map);
- 实现 👇
Map<String,Object> Map = new HashMap<>();
Map.put("pid",4);
delectPersonByIdWithProcedure(Map);
注意点👇
<transactionManager type="JDBC"/>
当 conf.xml 中的提交方式为手动时(JDBC)需要注意,每次的增删改都需要在最后commit一下!!!
五、resultType输入类型
1. 简单类型
2. 对象类型
当数据库字段与实体类属性名不同,或者类型不同时,用 resultMap
3. 对象集合类型
虽然输出类型为集合,但是resultType 任然是实体类型,而不是集合类型。 接口中是集合类型
4. HashMap类型
- 当返回值为HashMap时,能放多个元素,但只能放单一对象的多个元素
比如 Student(1,“zs”,23)只能存一个学生的多个元素
所以如果返回是多个对象,接口中的返回值类型应该写:List<Map<String, Object>>
六、resultMap输入类型
resultMap:实体类的属性、数据表的字段: 类型、名字不同时
注意:当属姓名和字段名不一致时,除了使用resultMap以外,还可以使用resultType+HashMap
1. resultMap
2. resultType+HashMap
注意
注意: 开发过程中如果有10个字段,但发现某一个字段结果始终为默认值(0,0.0,null),则可能是表的字段和类属性不匹配