他是一个持久层框架。可以实现对数据库的操作,可以看作是JDBC的高级版,解决JDBC的缺点。
能做什么
1)、注册驱动
2)、创建数据库的连接对象Connection、Statement、ResultSet
3)、执行sql语句,返回ResultSet
4)、处理ResultSet、把结果集中的数据转换为java对象,把java对象装进list集合中
5)、关闭数据库连接
6)、实现代码和sql语句的解耦。sql语句被单独提出来放在xml文件中
首先找一份MyBatis的中文官方文档看一遍,就能大概做一遍
首先创建一个mysql表test_01,列名自己设。
创建一个maven工程导入日志功能依赖、单元测试依赖junit、mysql驱动依赖、最重要的还有MyBatis依赖
4.0.0
org.example
MyBatis01
1.0-SNAPSHOT
8
8
mysql
mysql-connector-java
8.0.21
org.mybatis
mybatis
3.5.6
ch.qos.logback
logback-classic
1.2.3
junit
junit
4.12
test
在实际运行中不加这一段就不会把写在包下的xml文件加载进入实际运行的target目录中,测试就会报错
src/main/java
**/*.properties
**/*.xml
false
创建一个实体类,属性和数据表的列名一致,有参、无参、get、set、toString方法
创建一个Dao,创建一个方法用来做对数据库的操作
创建完毕后,首先在main-->>resource目录下创建一个mybatis配置文件,名字随便。里面主要写连接数据库的配置和指定一个MyBatis的mapper文件【里面主要存着sql语句,一般放在Dao包里,跟所属的Dao名字前缀一样。】
在MyBatisConfig.xml中
里面写的是连接Mysql的相关配置和对属性进行赋值,这一点很方便,让我觉得只要保证mysql驱动版本对的上,然后再保证属性值对的上,就能成功连接数据库,没有以前那么多命名、获取链接一些问题。
在写URL时会需要一些参数用到连字符&,但是xml文件中不支持,这时可以用html实体中代表这些特殊符号的替代字符串将想要串联的参数串联起来,【就是特殊符号如果出错就百度html实体,把特殊符号里换成html实体字符串】
除了配置JDBC连接的配置外,还有一个mapper 标签里面写的是存放sql语句的xml文件的相对路径,mapper标签属性值使用斜线划分的,就是在main目录下的路径这个文件一般跟Dao方法在一个包里面,名字尽量相似一点。
在TestUserSql.xml文件中
这里里面,mapper标签后面的 namespace是命名空间【namespace的值里面用点号划分】,让它指向你的Dao方法,在里面通过调用
类型别名<来自官方文档>
类型别名是你的好帮手。使用它们,你就可以不用输入类的全限定名了。比如:
然后就是单元测试
首先创建一个字符串,它的值是MyBatis的配置文件的名字。
使用Resources.getResourceAsStream获取到配置文件中的内容,并用inputStream对象接收。
然后用SqlSessionFactory的实现类SqlSessionFactoryBuilder().build(inputStream)将获取到的配置文件内容解析加载。
调用sqlSessionFactory.openSession创建一个SqlSession对象。
下面定义的String字符串是实际上执行的Dao中的方法,就是拼接字符串,不拼直接写也行。
用SqlSessionFactory中的selectOne方法把定义的字符串放进去,输出一下,并关闭sqlsession对象
这一套流程是固定的,能改的就是那两字符串输出内容。多用用就会了,后面会有更简洁的方法
@Test
public void test01() throws IOException {
String resource = "MyBatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
String sqlId = "com.xlw.testMyBatis.dao.TestUserDao"+"."+"SelectUserById";
TestUser testUser = sqlSession.selectOne(sqlId);
System.out.println(testUser);
sqlSession.close();
}
在保存着sql语句的xml配置文件中,进行操作数据库时,有时需要查询多个值,不能一直去更改数据,就可以使用占位符 #{自己看的懂得代称} 然后再通过调用方法时传入这个操作条件代表的值就可以省下功夫【一个参数时占位符随便叫,两个就要用@Param注解在参数和占位符之间建立映射】
在xml文件中
a)、在pom.xml文件中导入依赖
之后就能在控制台中看见运行的具体过程和一些日志
ch.qos.logback
logback-classic
1.2.3
b)、修改配置文件
在MyBatis的配置文件中添加以下内容,就可以在输出下面看见日志
在保存sql的配置文件中使用insert、update、delete都需要进行提交事务,才能实现对数据库的修改,否则尽管可以在输出中看见sql执行成功,但是实际上没有对数据库做出任何修改。 在控制台输出中我们通过日志发现 Setting autocommit to false ,即MyBatis中的自动提交事务默认是关闭的,需要自己调用commit方法手动提交
在sql.xml文件中
insert into test_01 values (null,"寒江孤影","江湖故人","[email protected]")
在测试类中
@Test
public void test02() throws IOException {
String resource = "MyBatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
String sqlId = "com.xlw.testMyBatis.dao.TestUserDao" + "." + "AddUser";
int i = sqlSession.insert(sqlId);
System.out.println(i);
// 在MyBatis中提交事务默认是手动提交,只执行sql语句不提交事务是无法完成添加insert、删除delete、修改update操作的
// 这个方法就是手动提交事务
sqlSession.commit();
sqlSession.close();
}
insert操作使用占位符
在上面进行insert操作时,没有使用占位符,为了省去修改操作,就可以使用占位符进行填充,在实际使用时再对占位符进行填充
insert into test_01 values (null,#{test_name},#{test_password},#{test_email})
在测试方法中
新建一个Object类型的对象,使用对应数据库表的实体类对象作为这个对象,调用对应的set方法设置对应的值。然后MyBatis在执行时会执行占位符中属性对应的get方法,将值拿出来放在那个占位符对应的位置,建议属性和占位符中名字要一样。
@Test
public void test02() throws IOException {
String resource = "MyBatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
String sqlId = "com.xlw.testMyBatis.dao.TestUserDao" + "." + "AddUser";
TestUser testUser = new TestUser();
testUser.setTest_name("水果捞");
testUser.setTest_password("123abc");
testUser.setTest_email("[email protected]");
int i = sqlSession.insert(sqlId,testUser);
System.out.println(i);
// 在MyBatis中提交事务默认是手动提交,只执行sql语句不提交事务是无法完成添加insert、删除delete、修改update操作的
// 这个方法就是手动提交事务
sqlSession.commit();
sqlSession.close();
}
有时候会出现对应的sql.xml和配置文件不能被编译到具体执行的文件夹target下,解决办法
1、删了target重新编译
2、清除IDEA的缓存并重启IDEA
3、使用maven窗口提供的clean,再compile,跟第一种方式差不多,这个优雅点
4、点击Build重新编译本项目
自动提交事务
在SqlSession中可以发现有很多OpenSession方法,其中有一个SqlSession openSession(boolean var1);的方法。在调用OpenSession方法时添加参数true,就能将事务提交改为自动提交。
在SqlSession中提供了方法来完成对数据库的操作。首先使用sqlSessionFactory的方法OpenSession获取到sqlSession对象,在调用这些方法。
多用就记住了
//它的作用就只是解析配置文件
String resource = "MyBatisConfig.xml";
//它的作用是获取一个inputStream对象
InputStream inputStream = Resources.getResourceAsStream(resource);
//SqlSessionFactoryBuilder和它的方法build作用就是得到一个sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//最后SqlSessionFactory对象是为了通过Opensession得到Sqlsession对象并调用它的方法
SqlSession sqlSession = sqlSessionFactory.openSession();
insert:插入一条记录
seleectOne:查询并最多返回一条记录,多了就报错
selectList:根据sql语句查询并返回一个list集合
selectMap:返回一个Map集合
update:根据sql和参数更新一条数据
delete:根据sql删除一个记录
commit:提交事务
rollback:回滚事务
由于DefaultSqlSession并不是线程安全的,所以使用时建议在方法内部创建SqlSession对象调用方法执行sql语句,方法内是在同一线程中
Sqlsession是一个重量级对象,他创建时需要获取connection连接对象,需要的时间比较长,一般项目中只需要一个这样的对象就可以了
新建一个maven工程,导入依赖
创建好bean类和Dao类
现在需要创建mapper和config文件,但是只能通过创建普通文件的形式创建。为了免除麻烦,可以自己在IDEA中分别定义一个Mapper和Config模板
找到settings-->>Editor-->>file and code templates ,点击加号创建一个新的模板名字起的自己一看就知道是那个就可以,然后改一下后缀为xml,在里面把之前第一次用的时候创建的那些复制进去,留下大概的框架剩下的删掉就行了。 创建两个模板一个是mapper一个是config
创建好了模板,使用你添加的模板建立配置文件,做好相关配置
注意命名空间的路径之间是用点区分,查询标签的id和返回结果集指定类型的bean对象的路径也要用点区分
再创建一个util工具类包
里面创建MyBaits的工具类
package xlw.com.useUtil.util;
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;
public class MyBatisUtil {
/**
* 首先定义一个字符串代表MyBatis配置文件。按照之前的步骤先通过Resources.getResourceAsStream获取一个InputStream对象
* 再通过SqlSessionFactoryBuilder().build(inputStream);获取一个SqlSessionFactory对象
* 由于这个过程要获取链接,需要先加载执行
*/
private static SqlSessionFactory sqlSessionFactory = null;
static{
String config = "MyBatis02Config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(config);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过SqlSessionFactory对象的openSession方法获取SqlSession对象
* @return 获取到的sqlSession对象,通过它调用操作方法
*/
public SqlSession getSqlSession(){
SqlSession sqlSession = null;
if (sqlSessionFactory != null){
sqlSession = sqlSessionFactory.openSession();
}
return sqlSession;
}
}
使用工具类上
只要调用工具类的方法,建立一个SqlId,调用方法最后关闭sqlsession对象
public class TestUtils {
@Test
public void test01(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String sqlId = "xlw.com.useUtil.dao.UserDao" + "." + "selectById";
User user = sqlSession.selectOne(sqlId, 1);
System.out.println("user = " + user);
sqlSession.close();
}
}
在UserDao中
package xlw.com.useUtil.dao;
import xlw.com.useUtil.bean.User;
import java.util.List;
public interface UserDao {
/**
* 查询单个记录
* @param id
* @return 查询到的数据
*/
User selectById(Integer id);
/**
* 查询全部
* @return 所有记录
*/
List getAllUser();
Integer AddUser(User user);
}
在DaoImpl中
在这里注意关闭连接和一些操作的提交事务,在mapper文件中的标签里面写的id要和这里面的id对上
public class UserDaoImpl implements UserDao {
private String nameSpace = "xlw.com.useUtil.dao.UserDao" + ".";
private SqlSession sqlSession = MyBatisUtil.getSqlSession();
@Override
public User selectById(Integer id) {
String sqlId = nameSpace + "selectById";
User user= sqlSession.selectOne(sqlId,id);
sqlSession.close();
return user;
}
@Override
public List getAllUser() {
String sqlId = nameSpace + "getAllUser";
List users = sqlSession.selectList(sqlId);
sqlSession.close();
return users;
}
@Override
public Integer AddUser(User user) {
String sqlId = nameSpace + "AddUser";
int line = sqlSession.insert(sqlId, user);
sqlSession.commit();
sqlSession.close();
return line;
}
}
在mapper文件中
insert into test_01
values (#{test_id}, #{test_name}, #{test_password}, #{test_email})
在测试类里面
public class TestDaoImpl {
UserDao userDao = new UserDaoImpl();
@Test
public void testSelect(){
User user = userDao.selectById(1);
System.out.println(user);
}
@Test
public void testSelectAll(){
List allUser = userDao.getAllUser();
for (User user:allUser){
System.out.println(user);
}
}
@Test
public void testAdd(){
User user = new User();
user.setTest_id(null);
user.setTest_name("成吉思汗");
user.setTest_password("123abc");
user.setTest_email("[email protected]");
Integer line = userDao.AddUser(user);
System.out.println("line = " + line);
}
}
在之前使用DaoImpl实现Dao方法最后完成方法想实现的功能,通过分析发现,如果使用反射机制可以获取到Dao的类路径和里面的方法,通过方法返回值可以判断实现具体功能执行的方法和方法所需要的SqlId。于是MyBatis就产生了一个技术:Dao代理。由MyBatis框架提供与DaoImpl类相同的功能,省去了创建DaoImpl类的步骤。这个过程不用sqlId
在使用时通过sqlSession.mapper(Dao.class)就可以得到一个Dao接口的代理对象,通过这个代理对象调用接口里面的方法。就行了,再就是输出返回值
使用代理的前提是在mapper文件中命名空间必须是Dao的全类名,操作标签的id都得是代表的方法名,要保持一致,cv行进
在mapper文件中
insert into test_01
values (#{test_id}, #{test_name}, #{test_password}, #{test_email})
在测试类中
没有sqlId
public class TestDaoImpl {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
@Test
public void testProxy(){
UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
System.out.println(userDaoProxy);
List allUser = userDaoProxy.getAllUser();
for (User user : allUser){
System.out.println(user);
}
sqlSession.close();
}
}
1、使用parameterType
在mapper标签的参数中使用parameterType属性赋值,有两种用法,一种是全限定类名,一种是别名
使用全类名
就是告诉MyBatis在Dao中形参的类型是什么类型,MyBatis会调用对应的set类型的方法为形参赋值
使用别名
别名不是自己起的别名在官方文档中有说明,什么类型的别名是什么
一般不用写这个parameterType,了解一下说不定就用上了
类型对应的别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
2、使用@param注解
@Param注解是MyBatis提供的注解,用来修饰Dao方法的形参,将形参和sql中的占位符建立映射关系
使用这个注解的效果就是条件越多结果越准确
使用
当方法里面有多个普通类型的参数时
User selectById( @Param("zhanweifu") Integer id,@Param("lala") String name);
需要保证注解里的value值和占位符里的自定义的一样,cv
3、使用对象作为参数
当sql需要很多参数,就不能一直使用注解@Param,可以用一个bean对象作为形参传递sql操作需要的值。作为形参的bean里面每个属性要有get和set方法,并且在mapper中sql语句占位符的名字要和bean里面的属性名一致
在mapper中
在用占位符时,我用email属性作为name的值
在测试中
上面用email作为name,在测试里用email的set方法装name的值,sql会根据语句中的查询列名进行查询。只是查询name列里面的值时借用了email的壳。这说明,随便哪一个有get和set方法的bean都能作为Dao方法的形参,里面的属性爱叫什么叫什么,只要类型和get、set不少,就可以作为Dao形参使用
@Test
public void testParameterType(){
UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
User user1 = new User();
user1.setTest_id(2);
user1.setTest_name("水果捞");
user1.setTest_email("五尺剑君子");
List list = userDaoProxy.selectByBean(user1);
System.out.println(list);
sqlSession.close();
}
小异常
今早学习时,遇见的异常信息Invalid bound statement:无效的绑定语句【指的就是使用实现类时需要的sqlId】,说是找不到Dao对应的方法。由于此时使用的是getMapper,需要mapper配置文件中的标签的id跟使用这个sql的Dao方法名一致,所以问题就是mapper中的标签里面的id跟Dao中的方法名不一样,导致的异常。
自定义对象作为形参
保证类型和get、set方法就能使用
自定义bean
提供属性和get、set方法
public class Nipusth {
private Object a;
private Object b;
public Object getA() {
return a;
}
public void setA(Object a) {
this.a = a;
}
public Object getB() {
return b;
}
public void setB(Object b) {
this.b = b;
}
}
在mapper中
使用自定义bean作为形参传递sql操作参数时,要保证sql占位符里面的值和bean中属性值相同
在test方法中
@Test
public void testParameterType(){
UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
Nipusth nipusth = new Nipusth();
nipusth.setA(2);
nipusth.setB("水果捞");
List list = userDaoProxy.selectByBean(nipusth);
System.out.println(list);
sqlSession.close();
}
复杂的占位符声明
属性
javaType |
一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
---|---|
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
BIT |
FLOAT |
CHAR |
TIMESTAMP |
OTHER |
UNDEFINED |
---|---|---|---|---|---|
TINYINT |
REAL |
VARCHAR |
BINARY |
BLOB |
NVARCHAR |
SMALLINT |
DOUBLE |
LONGVARCHAR |
VARBINARY |
CLOB |
NCHAR |
INTEGER |
NUMERIC |
DATE |
LONGVARBINARY |
BOOLEAN |
NCLOB |
BIGINT |
DECIMAL |
TIME |
NULL |
CURSOR |
ARRAY |
在使用时
一般简单的sql语句MyBatis会自己判断出javaType和jdbcType,不用自己设置,但是复杂的sql语句就需要自己声明,以便达到预期效果。就比如使用map
4、按位置为参数赋值
在Dao中
List selectByPosition(Integer id,String name);
arg代表参数,根据Dao参数的位置为参数赋值,不常用。如果参数变动,或者添加条件,需要调整很多,而且也不够明显。
5、使用Map集合传递参数值
在Dao中
List selectByMap(Map map);
在mapper中
占位符值要跟map中的key值相同
测试中
@Test
public void testMap() {
UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
Map map = new HashMap<>();
map.put("name","水果捞");
map.put("id",2);
List list = userDaoProxy.selectByMap(map);
System.out.println(list);
sqlSession.close();
}
并不建议使用Map,因为Map作为参数时类型是
6、#{}和${}的区别
#{}
#{}的使用是#{字符},他会被解析为问号。
解析:
String sql = "select * from test_01 where test_id = ? or test_name = ?"
调用jdbc的prepareStatement对象执行sql语句,效率高
PrepareStatement psm = conn.prepareStatement(sql);
psm.setXXX();
ResultSet rs = psm.executeQuery();
使用的PrepareStatement对象,能避免sql语句,sql语句更安全
#{}常常被用来当作列值使用【在sql中的占位符所在的位置就会调用getXXX()】,值和数据类型有关。int--->>setInt()
${}
${}的使用和#{}使用一样。使用${}时,解析就会成为拼接字符串,会被当作表里面的列
String sql = "select * from where test_id = +" 值 " or test_name = +" 值 " 使用Statement对象执行sql,效率低 Statement stem = conn.createStatement(sql); ResultSet rs = stem.executeQuery(); 使用${}占位符的值使用的是拼接字符串的形式,所以在赋值的过程可以在值里面再写sql语句,拿到其他数据,这样就有sql语句安全问题 使用${}你给占位符赋的什么值$就会原封不动的加载条件后面【不会调用getXXX方法】。在能保证sql安全时可以使一下
使用${}时,sql后面占位符的值要用注解@Param指定为那个参数赋值
使用
List selectK(@Param("oid") Integer id,@Param("oname") String name);
如果再方法里直接写参数值,不管sql语法就会出现异常 java.sql.SQLSyntaxErrorException: Unknown column '水果捞' in 'where clause',说没有找到列。
就要自己给varchar类型的列的值加单引号。
@Test
public void testParameterType1() {
UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
List list = userDaoProxy.selectK(2, "水果捞");
System.out.println(list);
sqlSession.close();
}
sql安全--给参数写sql语句,拿多余的数据
前面有说到${}是以拼接字符串的形式把你为参数赋的值原封不动的连接到sql中,所以在使用时会有sql安全问题,需要保证sql安全问题。否则就能通过一些巧妙地方法拿到很多数据。
@Test
public void testParameterType1() {
UserDao userDaoProxy = sqlSession.getMapper(UserDao.class);
List list = userDaoProxy.selectK(2, "'水果捞' + 'or id > 0'");
System.out.println(list);
sqlSession.close();
}
${}具体应用
${}代表列名、表名
可以应用于操作表中有多个相同的值并根据不同列名进行排序等操作,也能用来代替表名,切换操作的数据库表
使用
在Dao中
多个参数就要使用@Param注解建立映射
List selectByPlaceHolder(@Param("tableName") String tableName, @Param("listName") String ListName, @Param("test_name") String name);
在mapper中
在测试方法中
通过${}占位符设置操作目标和设置排序的条件
@Test
public void testPlaceHolder(){
UserDao userProxy = sqlSession.getMapper(UserDao.class);
List users = userProxy.selectByPlaceHolder("test_02","test_id", "五尺剑君子");
for (User user : users) {
System.out.println("user = " + user);
}
}
7、封装输出结果
ResultType的值是希望返回的类型的bean的全限定类名。在MyBatis执行sql时,把resultType中的数据转换全限定路径指定的bean类型对象
MyBatis会做以下操作:
1、调用全限定类路径指向的bean的无参构造方法,创建一个bean对象
2、此时MyBatis已经根据类型为sql中的占位符执行了对应的set类型方法,然后调用get方法得到占位符的值,再调用bean对象中属性的set方法,将同名的列赋值给同名的属性。【也就是为什么bean中的属性名要和列名相同】
3、得到bean对象,根据方法返回值对bean对象进行处理。是List就装进去,是bean对象就直接输出。
别名
在MyBatis的配置文件中,使用typeAliases标签设置别名,然后在mapper文件中的resultType中调用这个别名代表的实体类
第一种定义别名的方法
这种方法定义别名的优点是能自己随便写别名,缺点是有多个对象时要创建多个语句分别起别名。
第二种定义别名的方法
这种方法能直接让MyBatis把这个包下面的所有类的类名作为别名,不区分大小写。
缺点:别名不能自定义,而且如果设置了多个包,有一个bean在a包下面有,b包下面也有,这时就会产生错误。MyBatis不能区分那个别名是指代的bean。自己玩玩就别用别名,至少看上去一目了然,别把自己套烂了。
resultType的默认规则
resultType里面只要是一个bean,有属性和属性的set、get方法就行。不一定非要用表对应的bean类,只是用了比较方便。而且如果随便一个bean中的属性名和列名不一致,最后拿到的值是不能赋值给这个随便的bean的属性的,所以列名和属性名要一致
8、resultType返回简单类型的数据
使用
在Dao中
Integer selectCount();
在mapper中
使用其他类型作为返回值也是这么用
返回Map类型数据
在Dao中
Map
在mapper中
测试
使用Map作为返回值时,sql的返回值只能有一个值,多了就会出错,使用的是selectOne方法。
@Test
public void testSelectCountItem(){
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map
9、resultMap建立属性和列名映射关系
Dao中
返回值跟建立映射的bean是同一个
Grenword selectById1(Integer id);
在mapper中
使用resultMap之前要先用resultMap标签设置好列明和属性间的对应关系。
在包含sql语句的标签里resultMap和resultType只能用一个
使用上没什么区别
10、定义列别名
在使用时,属性名和列名不一样。想让属性接收这个列的值。
1、使用resultMap
2、使用resultType,在sql语句里面给列名起别名,别名跟属性名一致也行
在mapper中
11、模糊查询like
第一种方法:
直接在使用时对字符串做填充就可以,能够自己更改模糊查询的条件,建议使用
在Dao中
List selectLikeOne(String name);
在mapper中
第二种方法:
写死sql,where 条件 like "%"空格别名空格"%"
使用时如果想要修改条件就要在mapper中修改sql
在MyBaits中提供了很多标签帮助完成sql拼接工作。用的时候跟JSTL标签库差不多
有if、where、choose、trim、foreach标签
if标签
使用时能通过改变标签里的test属性值更改条件。达成sql操作目的
但是要注意拼接细节,使用复杂sql时要提前设计好,不然有些条件通过有些条件不通过最后拼起来的sql有语法错误
使用If标签时,处理的Dao方法,参数要是一个实体类
Dao中
在sqlMapper中
在sql语句后面加test_id != 0,就是处理拼接sql,在处理时,尽量添加一个不会影响结果的语句,或者一开始就设计好
测试
@Test
public void test01(){
SqlSession sqlSession = MyBatisConfigUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setTest_name("水果捞");
List users = mapper.selectIf(user);
for (User user1 : users) {
System.out.println("user1 = " + user1);
}
sqlSession.close();
}
where标签
where就是用来处理if标签带来的语法问题
在使用时,在where标签里面套if标签,如果if的条件符合通过,where标签就会转化为一个 WHERE关键字,在if标签里面的sql语句前面。并且会把if标签里面的sql语句前面和WHERE关键字拼接的部分带的or、and等条件去掉,直接是WHERE sql语句
如果if标签里的条件不通过,那么只会执行where标签前面的那一部分。
在Dao中
List selectWhere(User user);
在sqlMapper中
在输出台上可以发现
通过不停调整条件发现,WHERE后面的sql语句前面带的or 被去掉了
test_name符合条件
Preparing: select * from test_02 WHERE test_name = ?
两个条件都符合
Preparing: select * from test_02 WHERE test_id = ? or test_name = ?
test_id符合条件
Preparing: select * from test_02 WHERE test_id = ?
条件都不符合
Preparing: select * from test_02
forEach标签
forEach标签能遍历数组和List集合,尤其使用在in条件语句中
1、简单list进行遍历
在Dao中
List selectForEachOne(List idList);
在sqlMapper中
collection属性是代表遍历的是list还是array。
open是指in后面遍历的开始符号,separator属性是遍历sql里面的分隔符,close属性是遍历sql的结束符号,
item属性值 是自定义的,是实际遍历的集合或数组的名字
在测试中
@Test
public void test03(){
SqlSession sqlSession = MyBatisConfigUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(12);
List users = mapper.selectForEachOne(list);
for (User user : users) {
System.out.println("user = " + user);
}
sqlSession.close();
}
结果语句
Preparing: select * from test_02 where test_id in ( ? , ? , ? , ? )
2、使用对象类型的集合遍历
在Dao中
List selectForEachTwo(List userId);
在sqlMapper中
遍历的内容就是对象中的属性,属性名要一致
在测试中
@Test
public void test04(){
SqlSession sqlSession = MyBatisConfigUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List userList = new ArrayList<>();
User user = new User();
user.setTest_id(1);
User user1 = new User();
user1.setTest_id(12);
userList.add(user);
userList.add(user1);
List userList1 = mapper.selectForEachTwo(userList);
for (User user2 : userList1) {
System.out.println("user2 = " + user2);
}
sqlSession.close();
}
结果sql
Preparing: select * from test_02 where test_id in ( ? , ? )
Sql代码段
在sqlMapper中使用标签
在sqlMapper中
sql标签的id写到include标签的refid值里面
select * from test_02
test_id,test_name,test_password,test_email
结果输出
Preparing: select * from test_02 where test_id in ( ? , ? , ? , ? )
Preparing: select test_id,test_name,test_password,test_email from test_02 where test_id in ( ? , ? )
a)、properties标签引入外部配置文件
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3308/mybatis?characterEncoding=utf8&useSSL=true&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
MyBatisConfig中
b)、setting标签
一般不需要更改内容,使用默认值就可以
一个配置完整的 settings 元素的示例如下:
c)、类型别名(typeAliases)
在MyBatis的配置文件中,使用typeAliases标签设置别名,然后在mapper文件中的resultType中调用这个别名代表的实体类
第一种定义别名的方法
这种方法定义别名的优点是能自己随便写别名,缺点是有多个对象时要创建多个语句分别起别名。
第二种定义别名的方法
这种方法能直接让MyBatis把这个包下面的所有类的类名作为别名,不区分大小写。
缺点:别名不能自定义,而且如果设置了多个包,有一个bean在a包下面有,b包下面也有,这时就会产生错误。MyBatis不能区分那个别名是指代的bean。自己玩玩就别用别名,至少看上去一目了然,别把自己套烂了。
d)、环境配置
了解一下知道自己用的代表什么就行
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
如果忽略了环境参数,那么将会加载默认环境,如下所示:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
environments 元素定义了如何配置环境。
注意一些关键点:
默认使用的环境 ID(比如:default="development")。
每个 environment 元素定义的环境 ID(比如:id="development")。
事务管理器的配置(比如:type="JDBC")。
数据源的配置(比如:type="POOLED")。
默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
就是交给MyBatis管理事务,对事务进行回滚和提交操作。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行
为。例如:
提示 如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
public interface TransactionFactory {
default void setProperties(Properties props) { // 从 3.5.2 开始,该方法为默认方法
// 空实现
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
在事务管理器实例化后,所有在 XML 中配置的属性将会被传递给 setProperties() 方法。你的实现还需要创建一个 Transaction 接口的实现类,这个接口也很简单:
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
使用这两个接口,你可以完全自定义 MyBatis 对事务的处理。
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
url
– 这是数据库的 JDBC URL 地址。
username
– 登录数据库的用户名。
password
– 登录数据库的密码。
defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。
defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout()
的 API 文档以获取更多信息。
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:
driver.encoding=UTF8
这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8
的 encoding
属性给数据库驱动。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。属性值是这个则MyBatis使用了连接池
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10
poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。
poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections
与 poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5)
poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
poolPingEnabled
– 是否启用侦测查询。若开启,需要设置 poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
poolPingConnectionsNotUsedFor
– 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
initial_context
– 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:
env.encoding=UTF8
这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8
的 encoding
属性。
你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory
来使用第三方数据源实现:
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
可被用作父类来构建新的数据源适配器,比如下面这段插入 C3P0 数据源所必需的代码:
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
public C3P0DataSourceFactory() {
this.dataSource = new ComboPooledDataSource();
}
}
为了令其工作,记得在配置文件中为每个希望 MyBatis 调用的 setter 方法增加对应的属性。 下面是一个可以连接至 PostgreSQL 数据库的例子:
e)、mapper标签
mapper标签用来指定sqlMapper的路径,有两种方式指定mapper文件
第一种:使用xml文件的全路径
使用全路径的好处是,能清晰的看见文件在哪。缺点是,如果mapper文件多了,要 写很多路径,管理起来很麻烦
第二种:使用package标签,扫描整个包。
前提是Dao和mapper文件的名字要一样,要在同一个目录
两个二选一
首先引入pagehelper依赖
com.github.pagehelper
pagehelper
5.2.0
然后在 MyBatis配置文件中添加
然后在使用时调用PageHelper类里面的startPage方法
startPage方法里面的参数,第一个是第几页,第二个参数是每页展示的数据。在使用前先自己看看表里面有多少数据,再进行分页查询。一般在页面展示内容时要做分页操作,使用这个工具能减少很多工作,用的不顺手就自己写一个分页工具类
@Test
public void test05() {
SqlSession sqlSession = MyBatisConfigUtil.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
PageHelper.startPage(3, 50);
List allUser = mapper.getAllUser();
for (User user : allUser) {
System.out.println("user = " + user);
}
sqlSession.close();
}
结果输出
在mapper中的sql语句是select * from test_02,使用了pagehelper后语句变成了Preparing: select * from test_02 LIMIT ?, ? | Parameters: 100(Long), 50(Integer)
就能看见添加了limit关键字和查询区间,区间被计算好给放进sql中供其使用