MyBatis从入门到最后会讲解的内容:
基础知识(重点,内容量多)
对原生态jdbc程序(单独使用jdbc开发)问题总结
mybatis框架原理 (掌握)
mybatis入门程序
用户的增、删、改、查
mybatis开发dao两种方法:
原始dao开发方法(程序需要编写dao接口和dao实现类)(掌握)
mybaits的mapper接口(相当于dao接口)代理开发方法(掌握)
mybatis配置文件SqlMapConfig.xml
mybatis核心:
mybatis输入映射(掌握)
mybatis输出映射(掌握)
mybatis的动态sql(掌握)
高级知识
订单商品数据模型分析
高级结果集映射(一对一、一对多、多对多)
mybatis延迟加载
mybatis查询缓存(一级缓存、二级缓存)
mybaits和spring进行整合(掌握)
mybatis逆向工程
创建MySQL数据
导入下边脚本
sql_table.sql:记录表结构。
sql_data.sql:可以理解为记录测试数据。在实际企业开发中,相当于提供了一个初始化数据脚本。
下面来回顾一下JDBC里面的原始代码:
@Test
public void showUser(){
//数据库连接
Connection connection = null;
//预编译的Statement,使用预编译的Statement提高数据库性能
PreparedStatement preparedStatement = null;
//结果 集
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql");
//定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
//获取预处理statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while(resultSet.next()){
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
JDBC编程步骤:
1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建jdbc statement对象
4、 设置sql语句
5、 设置sql语句中的参数(使用preparedStatement)
6、 通过statement执行sql并获取结果
7、 对sql执行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)
JDBC问题总结:
1.数据库连接,使用时创建,不适用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响 数据库性能。
设想:使用数据库连接池管理数据库连接。
2.将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
3.向PreparedStatement中设置参数,对占位符位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。
4.从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。
Mybatis框架
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
- MyBatis 以前是在apache官网下载,之后被托管到googlecode下,再后来托管到GitHub下(https://github.com/mybatis/mybatis-3/releases)。
- MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
- Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
- 其中比较重要的一点就是:1.MyBatis 让程序员主要精力放在sql上,通过MyBatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql) 满足需要的sql语句。2.MyBatis 可以将向 PreparedStatement 中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
MyBatis框架思路:
现在我们有个需求:
本篇博客就将按照下面内容作为驱动案例讲解:
1.根据用户id(主键)查询用户信息
2.根据用户名称模糊查询用户信息
3.添加用户
4.删除 用户
5.更新用户
开发环境配置:
第一步:
导入jar包。
其中你会看到的目录结构:
在lib目录下的jar包:
把核心包和依赖包都导入。
还有别忘了导入MySQL驱动包。
第二步:
- log4j.properties
在classpath下创建log4j.properties,内容如下:
classpath就是src下边
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
第三步:
配置SqlMapConfig.xml
在src下创建SqlMapConfig.xml,如下:
<?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>
<!-- 和spring整合后 environments配置将废除-->
<!--
下面的两个标签environments和environment对应的属性default和id能够填入的值:
work:工作模式
development:开发模式
注意:两个属性值不能够乱写
-->
<environments default="development">
<!--
这里可以配置多个环境:
为什么可以配置多个环境?
因为在大型项目的时候可能会出现跨数据库存值取值的情况。
比如:我在北京数据库里面取出值,然后在上海数据库里面删除值。这有可能发生的。这种类型叫做JTA,即Java Transaction API。
-->
<environment id="development">
<!-- 使用jdbc事务管理,事务控制由MyBatis-->
<transactionManager type="JDBC" />
<!-- 配置数据源,其中数据源的实现是使用数据库连接池提供,由MyBatis管理-->
<dataSource type="POOLED">
<!--
数据源:
POOLED:表示使用连接池,自带的。
UNPOOLED:不使用连接池,每次使用连接后就断开。
MANAGED:使用容器管理。笔者也不知道这是什么。。
-->
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="" />
</dataSource>
</environment>
</environments>
</configuration>
第四步:
我们这里是查询User表内容,所以我们这里创建用户类:
public class User {
private Integer id;
private String username;
private Date birthday;
private Integer sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="
+ address + "]";
}
}
第五步:
编写用户类的映射文件。
映射文件命名:
User.xml(原始ibatis命名),mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.xml
我这里User类的映射文件名称为User.xml
在映射文件中配置sql语句。
<?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命名空间,作用就是对sql进行分类化管理,理解sql隔离
注意:使用mapper代理方法开发,namespace有特殊重要的作用,后面会讲到
-->
<mapper namespace="test">
<!-- 在 映射文件中配置很多sql语句 -->
<!-- 需求:通过id查询用户表的记录 -->
<!-- 通过 select执行数据库查询
id:标识 映射文件中的 sql
将sql语句封装到mappedStatement对象中,所以将id称为statement的id
parameterType:指定输入 参数的类型,这里指定int型
#{}表示一个占位符号
#{id}:其中的id表示接收输入 的参数,参数名称就是id,如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称
resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
-->
<select id="findUserByid" parameterType="int" resultType="cn.domarvel.entity.User">
select * from user where id=#{id}
</select>
</mapper>
第六步:
在MyBatis核心配置文件中加载映射文件
<!-- 加载映射文件 -->
<mappers>
<mapper resource="cn/domarvel/entity/User.xml"></mapper>
</mappers>
整体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>
<!-- 和spring整合后 environments配置将废除-->
<!--
下面的两个标签environments和environment对应的属性default和id能够填入的值:
work:工作模式
development:开发模式
注意:两个属性值不能够乱写
-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理,事务控制由MyBatis-->
<transactionManager type="JDBC" />
<!-- 数据库连接池,由MyBatis管理-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="" />
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="cn/domarvel/entity/User.xml"></mapper>
</mappers>
</configuration>
第七步:
编写测试代码
@Test
public void findUserById() {
SqlSession sqlSession=null;
try {
//以流的方式获取MyBatis的配置文件
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//创建会话工厂,传入MyBatis的配置文件信息
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession
sqlSession=sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace.statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
User user=sqlSession.selectOne("test.findUserByid", 1);
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}finally {
// 释放资源
sqlSession.close();
}
}
可以不通过命名空间.id加载sql语句
通过命名空间.id访问配置文件的sql语句不是绝对的,可以不通过命名空间.id访问,如果id是唯一的,那么命名空间可以省略。
例如:
<select id="findUserByid" parameterType="int" resultType="cn.domarvel.entity.User">
select * from user where id=#{id}
</select>
//如果findUserByid是唯一的
//那么可以在调用的时候把命名空间省略掉
- 1
- 2
- 3
- 4
- 5
这是笔者当前的工作目录:
在User.xml中进行配置以下内容:
<!-- 根据用户名称模糊查询用户信息,可能返回多条
resultType:指定就是单条记录所映射的java对象 类型
${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。
使用${}拼接sql,引起 sql注入
${value}:接收输入 参数的内容,如果传入类型是简单类型,${}中只能使用value
-->
<select id="findUserByLikeName" parameterType="java.lang.String" resultType="cn.domarvel.entity.User">
select * from user where username like '%${value}%'
</select>
测试代码:
@Test
public void findUserByLikeName(){
SqlSession sqlSession=null;
try {
//以流的方式获取MyBatis的配置文件
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//创建会话工厂,传入MyBatis的配置文件信息
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession
sqlSession=sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace.statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
List<User> list =sqlSession.selectList("test.findUserByLikeName", "小明");
for(User i:list){
System.out.println(i);
}
// 释放资源
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
这个是当前测试代码,MyBatis发出的sql底层代码。很明显通过${value}
这个关键字符串起的作用是把字符串和字符串拼接起来,并且没有用到占位符。所以很容易引起sql注入攻击。所以不建议使用${}
DEBUG [main] - ==> Preparing: select * from user where username like '%小明%'
下面来讲个区别(重点):
需求:我们想要在数据表里面找到“陈小明”这个User并且输出。
有两种实现:
@Override
public User findUserByNameMap(String name) throws Exception {
SqlSession sqlSession=sqlSessionFactory.openSession();
Map<String, User> resultsMap=sqlSession.selectMap("test.findUsers", "username");//第一个是声明操作id,第二个是指定表中的哪儿一个列名为键。
//查询结果为map,指定列名为键,查询结果为值的map
return resultsMap.get(name);
}
@Override
public User findUserByNameList(String name) throws Exception {
SqlSession sqlSession=sqlSessionFactory.openSession();
List<User> users=sqlSession.selectList("test.findUsers");
for(User i:users){
if(i.getUsername().equals(name)){
return i;
}
}
return null;
}
测试代码:
@Test
public void findUserByNameMap() throws Exception{
System.out.println(userDao.findUserByNameMap("陈小明"));
}
@Test
public void findUserByNameList() throws Exception{
System.out.println(userDao.findUserByNameList("陈小明"));
}
看了上面的代码实现,我就想问,哪儿一个效率比较高??
其实我一直都没有怎么注意这些,但是我被问起来的时候,就发现了它的重要性。
很明显,根据Map查询的时候效率高!!因为我是直接根据Map索引取值的。而List是还要遍历判断是否为需要的值,这是很浪费时间的。
Map的数据结构是散列表,也就是Hash表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
而Hash表一般就是下面这个结构,数组+链表。其中数组会自动扩容。
编写User.xml用户映射文件:
<!-- 添加用户
pojo是自定义类型的意思。
parameterType:指定输入 参数类型是pojo(包括 用户信息)
#{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
-->
<insert id="insertSingleUser" parameterType="cn.domarvel.entity.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
测试代码:
@Test
public void insertIntoUser(){
SqlSession sqlSession=null;
try {
//以流的方式获取MyBatis的配置文件
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//创建会话工厂,传入MyBatis的配置文件信息
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession
sqlSession=sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace.statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
sqlSession.insert("test.insertSingleUser", new User("FireLang", new Date(), 1, "LangIsland"));
//查询值的时候事务将会自动提交。添加值的时候事务是不会自动提交的,如果我们要提交事务需要自己手动提交。不提交事务,数据插入将不会成功。
sqlSession.commit();
// 释放资源
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
DEBUG信息:
DEBUG [main] - ==> Preparing: insert into user(username,birthday,sex,address) values(?,?,?,?)
DEBUG [main] - ==> Parameters: FireLang(String), 2017-04-04 15:20:10.507(Timestamp), 1(Integer), LangIsland(String)
mysql自增主键,执行insert提交之前自动生成一个自增主键。
通过mysql函数获取到刚插入记录的自增主键:
LAST_INSERT_ID()
是insert之后调用此函数。
我们要实现把自增主键查询后放入到User对象里面。
User.xml配置文件配置:
<!-- 添加用户
pojo是自定义类型的意思。
parameterType:指定输入 参数类型是pojo(包括 用户信息)
#{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
-->
<insert id="insertSingleUser" parameterType="cn.domarvel.entity.User">
<!--
将插入数据的主键返回,返回到user对象中
SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键
keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
resultType:指定SELECT LAST_INSERT_ID()的结果类型
-->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
测试代码:
@Test
public void insertIntoUser(){
SqlSession sqlSession=null;
try {
//以流的方式获取MyBatis的配置文件
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//创建会话工厂,传入MyBatis的配置文件信息
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession
sqlSession=sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace.statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
User user=new User("FireLangX", new Date(), 1, "LangIsland");
sqlSession.insert("test.insertSingleUser", user);
System.out.println("############ID= "+user.getId());
sqlSession.commit();
// 释放资源
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
DEBUG输出的部分信息:
DEBUG [main] - ==> Preparing: insert into user(username,birthday,sex,address) values(?,?,?,?)
DEBUG [main] - ==> Parameters: FireLangX(String), 2017-04-04 15:56:13.383(Timestamp), 1(Integer), LangIsland(String)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: select LAST_INSERT_ID()
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
############ID= 32
使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,长度设置成35位。
执行思路:
先通过uuid()查询到主键,将主键设置到 parameterType 指定的对象中
执行uuid()语句顺序相对于insert语句之前执行。
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
SELECT uuid()
</selectKey>
insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})
<!-- 注意上面的sql语句要写插入的id -->
User.xml配置文件:
<!--
删除用户:
根据id删除用户,需要输入id值
-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{idx}
</delete>
测试代码:
@Test
public void deleteUser(){
SqlSession sqlSession=null;
try {
//以流的方式获取MyBatis的配置文件
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//创建会话工厂,传入MyBatis的配置文件信息
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession
sqlSession=sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace.statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
sqlSession.delete("test.deleteUser", 33);
//必须手动提交事务
sqlSession.commit();
// 释放资源
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
<!-- 根据id更新用户
分析:
需要传入用户的id
需要传入用户的更新信息
parameterType指定user对象,包括 id和更新信息,注意:id必须存在
#{id}:从输入 user对象中获取id属性值
-->
<update id="updateUser" parameterType="cn.domarvel.entity.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
测试代码:
@Test
public void updateUser(){
SqlSession sqlSession=null;
try {
//以流的方式获取MyBatis的配置文件
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//创建会话工厂,传入MyBatis的配置文件信息
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession
sqlSession=sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace.statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
User user=new User("北斗", new Date(), 1, "FIREIsland");
user.setId(32);
sqlSession.update("test.updateUser", user);
//必须手动提交事务
sqlSession.commit();
// 释放资源
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
<?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命名空间,作用就是对sql进行分类化管理,理解sql隔离
注意:使用mapper代理方法开发,namespace有特殊重要的作用,后面会讲到
-->
<mapper namespace="test">
<!-- 在 映射文件中配置很多sql语句 -->
<!-- 需求:通过id查询用户表的记录 -->
<!-- 通过 select执行数据库查询
id:标识 映射文件中的 sql
将sql语句封装到mappedStatement对象中,所以将id称为statement的id
parameterType:指定输入 参数的类型,这里指定int型
#{}表示一个占位符号
#{id}:其中的id表示接收输入 的参数,参数名称就是id,如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称
resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
-->
<select id="findUserByid" parameterType="int" resultType="cn.domarvel.entity.User">
select * from user where id=#{id}
</select>
<!-- 根据用户名称模糊查询用户信息,可能返回多条
resultType:指定就是单条记录所映射的java对象 类型
${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。
使用${}拼接sql,引起 sql注入
${value}:接收输入 参数的内容,如果传入类型是简单类型,${}中只能使用value
-->
<select id="findUserByLikeName" parameterType="java.lang.String" resultType="cn.domarvel.entity.User">
select * from user where username like '%${value}%'
</select>
<!-- 添加用户
pojo是自定义类型的意思。
parameterType:指定输入 参数类型是pojo(包括 用户信息)
#{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
-->
<insert id="insertSingleUser" parameterType="cn.domarvel.entity.User">
<!--
将插入数据的主键返回,返回到user对象中
SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键
keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
resultType:指定SELECT LAST_INSERT_ID()的结果类型
-->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<!--
删除用户:
根据id删除用户,需要输入id值
-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{idx}
</delete>
<!-- 根据id更新用户
分析:
需要传入用户的id
需要传入用户的更新信息
parameterType指定user对象,包括 id和更新信息,注意:id必须存在
#{id}:从输入 user对象中获取id属性值
-->
<update id="updateUser" parameterType="cn.domarvel.entity.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
</mapper>
总结
parameterType
- 在映射文件中通过parameterType指定输入 参数的类型。
resultType
- 在映射文件中通过resultType指定输出结果的类型。
#{}和${}
#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型,pojo、hashmap。
为什么用#{}代表,而不用"?"代表占位符呢??
因为框架将sql和程序代码分离开,所以无法直接确认参数的含义,到底"?"代表哪儿个参数等等,所以框架采用了特殊的符号来代替"?"号。
如果接收简单类型,#{}中可以写成value或其它名称。
#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。
${}表示一个拼接符号,会引用sql注入,所以不建议使用${}。
${}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,${}中只能写成value。
${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。
这里要注意一点:如果我们传递过来的参数只是一个简单类型(例如String,Interger等,并没有封装多个属性的叫做简单类型,而封装多个属性并且提供了setter和getter的叫做封装类型。Map是封装类型,取值可以根据key取值。),并且有多个#{}(名称可以任意取,如#{aa}、#{bb}),那么这里的所有#{}占位符的最终获取值都相同。
selectOne和selectList
-
selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。
-
selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。
如果使用selectOne报错:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4