上一篇文章介绍了JDBC的基本使用,虽然改进写了工具类,但代码还是很多,开发效率低,自己还得注意Connection、Statement、PreparedStatement、ResultSet对象的创建和销毁,得到的结果集还需要自己封装处理,比较麻烦,业务代码和数据库操作混在一起,耦合度高。鉴于以上缺点,大牛们开发了Mybatis框架来解决这些问题。Mybatis框架就是一个封装好的JDBC,搬砖人只需要专注于写sql即可操作数据库。
官网对MyBatis的定义如下:
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
还是先从一个例子来入门Mybatis。同样,需要的依赖导入:
<dependencies> <dependency> <groupId>org.mybatisgroupId> <artifactId>mybatisartifactId> <version>3.5.1version> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> <version>5.1.47version> dependency> <dependency> <groupId>junitgroupId> <artifactId>junitartifactId> <version>4.11version> <scope>testscope> dependency> dependencies> <build> <resources> <resource> <directory>src/main/javadirectory> <includes> <include>**/*.propertiesinclude> <include>**/*.xmlinclude> includes> <filtering>falsefiltering> resource> <resource> <directory>src/main/resourcesdirectory> <includes> <include>**/*.propertiesinclude> <include>**/*.xmlinclude> includes> <filtering>falsefiltering> resource> resources> build>
建表如下:
1.建User表,建立与user表的映射关系。
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; } 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; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; }}
2.编写UserDao接口,用来编写操作数据库的方法
public interface UserDao { ListgetUserList();}
3.编写UserMapper.xml,sql映射文件,编写与UserDao接口中的方法对应的sql语句
<?xml version="1.0" encoding="UTF-8" ?>/span> PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.zhu.dao.UserDao"> <select id="getUserList" resultType="com.zhu.pojo.User"> select * from user; select>mapper>
4.编写主配置文件mybatis-config.xml,用来连接数据库以及指定mapper文件的位置
<?xml version="1.0" encoding="UTF-8" ?>/span> PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> dataSource> environment> environments> <mappers> <mapper resource="com/zhu/dao/UserMapper.xml"/> mappers>configuration>
5.测试
@Testpublic void test1() //1.读取配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //2.创建SqlSessionFactoryBuilder对象 sqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //3.创建sqlSessionFactory对象 sqlSessionFactory factory=builder.build(inputStream); //4.sqlSessionFactory生产SqlSession对象 SqlSession sqlsession=factory.openSession(); //5.sqlsession创建UserDao接口的代理对象 UserDao userdao = sqlsession.getMapper(UserDao.class); //6.代理对象执行查询方法 List userList = userdao.getUserList(); for (User user : userList) { System.out.println(user); } //7.释放资源 sqlsession.close(); inputStream.close();}
结果如下:
对这个例子做一下总结:它是基于XML配置文件的。从测试类开始看,读取mybatis-config.xml主配置文件,包含数据库连接的信息,在标签指定了sql映射文件的类路径:
<mappers> <mapper resource="com/zhu/dao/UserMapper.xml"/>mappers>
读取配置文件就读到了UserMapper.xml配置文件,它和dao接口同目录,标签中namespace指定了dao接口的路径,下面的各种标签用来编写sql语句:增、删、改、查。
下面的查询标签中,id是该标签即sql语句的唯一标志,resultType指定了执行语句的返回结果类型。
namespace= <select id="getUserList" resultType="com.zhu.pojo.User"> select * from user; select>
读取完配置文件之后就是各种创建对象。
首先看看官网对于SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession三个对象的解释:
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
大致逻辑就是从主配置文件可以获取SqlSessionFactoryBuilder对象,它可以创建工厂对象SqlSessionFactory,SqlSessionFactory对象可以生产SqlSession对象,SqlSession对象可以用来执行编写的sql。
最后,采用动态代理的方法获取dao对象的代理对象,代理对象执行dao接口中操作数据库的方法。采用动态代理的方法获取dao对象的代理对象有以下注意点:
- dao接口要和mapper文件放在同一目录下
- dao接口和mapper文件名称尽量一致
- mapper文件中的namespace的值是dao的全限定名称
- mapper文件中的增、删、改、查的id是dao接口中的方法名。
//5.sqlsession创建UserDao接口的代理对象UserDao mapper = sqlsession.getMapper(UserDao.class);//6.代理对象执行查询方法List<User> userList = mapper.getUserList();
上面的6步只是为了更加详细点写清楚每一步的逻辑,参照官网,以上代码可以简化如下:
String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlsession=factory.openSession();UserDao userdao = sqlsession.getMapper(UserDao.class);ListuserList = userdao.getUserList();for (User user : userList) { System.out.println(user);}
主配置文件在实际开发中也需要优化:
在主配置文件同目录建jdbc.properties文件:
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8jdbc.username=rootjdbc.password=root
主配置文件中引入jdbc.properties:
<configuration> <properties resource="jdbc.properties">properties> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> settings> <typeAliases> <typeAlias type="com.zhu.pojo.User" alias="user">typeAlias> <package name="com.zhu">package> typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> dataSource> environment> environments> <mappers> <mapper resource="com/zhu/dao/UserMapper.xml"/> mappers>configuration>
大致厘清Mybatis的执行思路之后,可以对代码进行封装来简化代码,编写MybatisUtils类:
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static{ try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getsqlsession(){ return sqlSessionFactory.openSession(); }}
测试代码如下:
@Testpublic void test1(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); List userList = userdao.getUserList(); for (User user : userList) { System.out.println(user); }}
上面介绍完来介绍一下其他的增删改查简单实现!
public interface UserDao { //查询所有 ListgetUserList(); //按指定Id查找 User findById(Integer integer); //增加一个用户 void insertUser(User user); //按照指定id更改 int updateUser(User user); //按照指定id删除 int deleteUser(Integer id);}
<?xml version="1.0" encoding="UTF-8" ?>/span> PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.zhu.dao.UserDao"> <select id="getUserList" resultType="com.zhu.pojo.User"> select * from user; select> <select id="findById" parameterType="Integer" resultType="com.zhu.pojo.User"> select * from user where id =#{uid} select> <insert id="insertUser"> insert into user (id,name,pwd)values(#{id},#{name},#{pwd}) insert> <update id="updateUser" > update user set name=#{name},pwd=#{pwd} where id=#{id} update> <delete id="deleteUser" > delete from user where id=#{id} delete>mapper>
public class UserDaoTest { @Test public void test1(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao mapper = sqlsession.getMapper(UserDao.class); List userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } } @Test public void test2(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); User user = userdao.findById(2); System.out.println(user); } @Test public void test3(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); User user=new User(); user.setId(4); user.setName("赵八"); user.setPwd("222544"); userdao.insertUser(user); sqlsession.commit(); } @Test public void test4() throws IOException { SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); User user = userdao.findById(4); user.setName("赵六六"); user.setPwd("22255444"); int res = userdao.updateUser(user); System.out.println(res); sqlsession.commit(); } //删除指定编号 @Test public void test5() throws Exception { SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); int res = userdao.deleteUser(4); sqlsession.commit(); System.out.println(res); }}
在上面的例子中,dao接口中的方法都是传递了一个参数,如果传递多个参数,传递参数有以下注意点:
- 传递一个简单的参数:#{任意字符}
- 传递多个简单类型的参数,使用@Param("自定义名称")
- 传递一个java对象,#{java对象的属性值},java对象要有构造方法和get方法
- 使用参数的位置,#{0},#{1},#{arg0},#{arg1}
- map作为参数,#{map的key}
最常用的是情况1和2,举个例子说明2:
//按照指定条件查询,查询id为 3或者姓名为张三的用户 List findByParam(@Param("myid") Integer id,@Param("myname") String name);
<select id="findByParam" resultType="com.zhu.pojo.User"> select * from user where id=#{myid} or name=#{myname} select>
@Test public void test21(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); List users = userdao.findByParam(3, "张三"); for (User user : users) { System.out.println(user); } }
#和$的区别:
#是占位符,表示列值,放在等号右侧,指定PreparedStatement对象执行sql,效率高,没有sql注入风险。
$占位符,表示字符串的连接,把sql语句连接成字符串。
这两者都可以用在模糊查询中:
第一种方式,#{}中#{}实现向preparedStatement占位符设置值,自动进行java类型和jdbc类型转换,可以有效防止sql注入。
public interface UserDao { ListfindByName(String name);}
<select id="findByName" parameterType="String" resultType="com.zhu.pojo.User"> select * from user where name like #{username}select>
@Test public void testfindbyname() throws Exception { SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); List users = userdao.findByName("%王%"); for (User user : users) { System.out.println(user); } }
第二种方式,${value}是固定的,基于statement,不进行java和jdbc的类型转换。
<select id="findByName" resultType="com.zhu.pojo.User"> select * from user where name like '%${value}%' select>
@Test public void testfindbyname() throws Exception { SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); List users = userdao.findByName("王"); for (User user : users) { System.out.println(user); } }
可以看出两种方法虽然结果相同,但实际执行的sql语句是不同的。
接着介绍一下Mybatis中参数设置。
parameterType:将会传入这条语句的参数的类全限定名或别名。传递的参数可以是基本类型,也可以是引用类型,还可以是实体类型(如User),还可以是实体类的包装类。如果注册了别名,可以直接用别名,常用的数据类型已经注册了别名。
这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset),开发中一般不写。
typeAliases:在SqlMapConfig.xml中配置
<typeAliases> <typeAlias type="com.zhu.pojo.User" alias="user">typeAlias> <package name="com.zhu">package>typeAliases>
扫描整个包下的类,指定别名后就不用了再写com.zhu.pojo.User了,别名就是类名,直接写user,首字母不区分大小写了。
resultType:期望从这条语句中返回结果的类全限定名或别名。 注意,可以是任意的类型,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。支持基本类型和实体类型。如果注册了别名,可以直接用别名。但是不建议使用别名,如果多个包有相同的类会出现错误。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。建立查询的列名和实体类名不一致时建立对应关系。举个例子:
新建实体类Myuser
public class Myuser { private int userId; private String userName; private String userPwd; 构造方法、get/set、toString方法省略 }
ListgetMyserList();
<resultMap id="myusermap" type="myuser"> <id column="id" property="userId">id> <result column="name" property="userName">result> <result column="pwd" property="userPwd">result>resultMap><select id="getMyserList" resultMap="myusermap"> select * from user;select>
<resultMap id="usermap" type="com.zhu.pojo.User"> column:列值,property:java对象的属性值 //主键用id <id column="id" property="userId">id> //非主键用result <result column="name" property="userName">result> <result column="pwd" property="userPwd">result>resultMap> <select id="findAll" resultMap="usermap"> select * from user select>
对于该例子,如果想达到同样结果,还可以这么做:
<select id="getMyserList" resultType="myuser"> select id as userId,name as userName,pwd as userPwd from user;select>
接着介绍动态sql。常用的标签有、、、、。
ListselectIf(User user);
<select id="selectIf" resultType="user"> select * from user where 1=1 <if test="name!=null and name!=''"> and name=#{name} if> <if test="id>0"> or id>#{id} if>select>
@Test public void test7(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); User user=new User(); user.setId(2); user.setName("朱5"); List users = userdao.selectIf(user); for (User u : users) { System.out.println(u); } }
<select id="selectWhere" resultType="user"> select *from user <where> <if test="name!=null and name!=''"> and name=#{name} if> <if test="id>0"> or id>#{id} if> where>select>
List selectForeach(Listlist);
<select id="selectForeach" resultType="user"> select *from user where id in <foreach collection="list" open="(" close=")" item="id" separator=","> #{id} foreach> select>
@Test public void test9(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao userdao = sqlsession.getMapper(UserDao.class); Listlist=new ArrayList(); list.add(3); list.add(7); list.add(8); List users = userdao.selectForeach(list); for (User u : users) { System.out.println(u); } }
这里集合中放的基本数据类型,如果放的是引用数据类型,标签里应改成如下
<foreach collection="list" open="(" close=")" item="id" separator=","> #{Student.id}</foreach>
sql片段:用来复用重复代码片段。
"mysql"> select * from user
"selectWhere" resultType= "mysql"> <where> <if test="name!=null and name!=''"> and name=#{name} if> <if test="id>0"> or id>#{id} if> where>
<select id="selectForeach" resultType="user"> "mysql"/> <foreach collection="list" open="(" close=")" item="id" separator=","> #{id} foreach> select>
在Mybatis中,还有个小知识点:分页,用到的是PageHelper插件。
导入依赖和插件:
<dependency> <groupId>com.github.pagehelpergroupId> <artifactId>pagehelperartifactId> <version>5.2.0version>dependency>
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"/>plugins>
<select id="getUserList" resultType="user"> select * from user order by idselect>
@Test public void test1(){ SqlSession sqlsession = MybatisUtils.getsqlsession(); UserDao mapper = sqlsession.getMapper(UserDao.class); PageHelper.startPage(1,3); List userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } }
至此,关于Mybatis框架的基于XML的基本使用介绍完毕。在第二篇中会介绍Mybatis如何多表查询、Mybatis延迟加载策略、Mybatis缓存以及基于注解开发使用Mybatis。