1.原始JDBC操作存在的问题
1.1原始操作流程
public static void main(String[] args) throws SQLException, ClassNotFoundException { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; //加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); //通过驱动管理类获取数据库链接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root"); //定义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")); } //释放资源 if (resultSet != null) resultSet.close(); if (preparedStatement != null) preparedStatement.close(); if (connection != null) connection.close(); }
1.2原生JDBC操作存在问题分析:
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
2、 Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
3、 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
4、 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
2.mybaties
2.1mybaties是什么?
mybatis是一个持久层的框架,是apache下的顶级项目。
mxbatis 托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)。
mybatis 让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。
mybatis 可以将向preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
2.2 Mybatis架构
1、 mybatis配置
SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
2.3工程搭建:
创建java工程
所需依赖:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <!-- <scope>test</scope>--> </dependency> <!-- spring-context:导入spring核心jar包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.2.RELEASE</version> <!-- <scope>test</scope>--> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!-- 导入日志jar包--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>1.3.4</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <!-- 动态代理jar包--> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version> </dependency> <!-- jdbcjar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.7.RELEASE</version> </dependency> <!-- 事务支持jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.7.RELEASE</version> </dependency> <!-- 数据源jar包--> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- 另外一种数据源jar包commons-dbcp和commons-pool;--> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.3.0</version> </dependency> <!-- mybaties依赖javassist--> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.20.0-GA</version> </dependency>
编写log4j.properties
在classpath下创建log4j.properties如下:
并将如下内容粘贴进去
# Global logging configuration #开发环境设置成DEBUG,生产环境设置成INFO或者ERROR 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
mybatis默认使用log4j作为输出日志信息。
上面的内容,在我们下载mybaties的时候,里面的帮助文档里面也可以找到:
创建SqlMapConfig.xml(名字不是SqlMapConfig也可以)
在classpath下创建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 default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC"/> <!-- 数据库连接池--> <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="admin123"/> </dataSource> </environment> </environments> </configuration>
SqlMapConfig.xml是mybatis核心配置文件,上边文件的配置内容为数据源、事务管理。
3.第一个查询例子:
需求:根据ID查询用户信息
写一个User.java类:
public class User { private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 ...get ...set ...toString
数据库创建对应的表
注意字段名要一一对应!!!并插入一条数据哦哦...
创建sql映射文件
在classpath下的sqlmap目录下创建User类的sql映射文件usersMapper.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命名空间,用于隔离sql语句,后面会讲另一层非常重要的作用。--> <mapper namespace="test"> <!--在映射文件中配置很多sql语句--> <!--需求;通过id查询用户表的记录--> <!--通过select执行数据库查询 id:标识映射文件中的sql将sql语句封装到mappedStatement对象中,所以将id称为statement的id parameterType:指定输入参数的类型,这里指定int型 #{}表示一个占位符号 #{id}:其中的id表示接收输入的参数,参数名称就是id,如果输入参数是简单类型,#{}中的参数名可以任意,可以value或其它名称(因为在运行过程中mybaties最终执行的sql的时候#{***}会被换成?) resultType:指定sql输出结果的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。 --> <select id="findUserById" parameterType="int" resultType="mybaties.pojo.User"> SELECT * FROM USER WHERE ID=#{id} </select> </mapper>
加载映射文件
mybatis框架需要加载映射文件,将usersMapper.xml添加在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> ... <!-- 加载映射文件--> <mappers> <mapper resource="sqlmap/userMapper.xml"></mapper> </mappers> </configuration>
测试程序:
import mybaties.pojo.User; 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 org.junit.Test; import java.io.IOException; import java.io.InputStream; public class _01Mybatis_first { @Test public void test() { // 1.配置文件 String mapper = "SqlMapConfig.xml"; SqlSession sqlSession = null; // 2.将配置文件加载到流 try { InputStream stream = Resources.getResourceAsStream(mapper); // 3.创建会话工厂,传入mybaties配置文件 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream); // 4.通过工厂得到sqlSession sqlSession = sessionFactory.openSession(); // 通过Sq1Session操作数据库 // 第一个参数:映射文件中statement的id,等于=namespace+"."+statement的id // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数 // sqlSession.selectOne()返回的结果就是映射文件中所匹配的resultType类型的对象 User user = sqlSession.selectOne("test.findUserById", 1); System.out.println(user); // } catch (IOException e) { e.printStackTrace(); } finally { // 释放资源 sqlSession.close(); } } }
4.根据用户名模糊查询用户数据:
userMapper.xml添加如下内容:
<!--根据用户名称模糊查询用户信息,可能返回多条 resultType:指定就是单条记录所映射的java对象类型 ${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sq1中。 使用${}拼接sql,引起sql注入漏洞 ${value}:接收输入参教的内容,如果传入类型是简单类型,${}中只能使用value --> <select id="findUserByName" resultType="mybaties.pojo.User" parameterType="java.lang.String"> SELECT *FROM USER WHERE USERNAME LIKE '%${value}%' </select>
测试代码:
@Test public void testByName() { String mapper = "SqlMapConfig.xml"; SqlSession sqlSession = null; try { InputStream stream = Resources.getResourceAsStream(mapper); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream); sqlSession = sessionFactory.openSession(); // List<User> list = sqlSession.selectList("test.findUserByName", "沁"); List<User> list = sqlSession.selectList("test.findUserByName", "' OR 1=1#"); //上面这行代码演示的是注入漏洞,最终会查询出user表所有的数据; // (sql语法:#后面的内容就是注释内容) // 所以,最终执行的SQL语句是:SELECT *FROM USER WHERE USERNAME LIKE '%' OR 1=1 System.out.println(list); } catch (IOException e) { e.printStackTrace(); } finally { sqlSession.close(); } }
5.插入用户信息:
userMapper.xml添加如下内容:
<!--添加用户 parameterType:指定输入参数类型是pojo(包括用户信息) #{}中指定poio的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值 --> <insert id="insertUser" parameterType="mybaties.pojo.User"> INSERT INTO USER (USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{username},#{birthday},#{sex},#{address}) </insert>
测试代码:
// 插入用户信息 @Test public void insertUser(){ SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法 User user = new User(); user.setUsername("刘悦"); user.setSex("1"); user.setBirthday(new Date()); user.setAddress("美国"); session.insert("test.insertUser",user); session.commit(); session.close(); }
6.插入用户信息并返回插入的最新数据的主键:
获取自增主键:
userMapper.xml添加如下内容:
<!-- 添加用户,并返回刚刚插入数据对应的自增主键--> <insert id="insertUserAndGetKey" parameterType="mybaties.pojo.User"> <!-- 将插入数据的主键返回,返回到user对象中 SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键 keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性 order:SELECT LAST_INSERT_ID()的执行顺序,相对于insert语句来说它的执行顺序 resultType:指定SELECT LAST_INSERT_ID()的结果类型 --> <selectKey keyProperty="id" resultType="java.lang.Integer" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO USER (USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{username},#{birthday},#{sex},#{address}) </insert>
演示代码:
// 插入用户信息并获取新插入数据的自增主键 @Test public void insertUserAndGetKey(){ SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法 User user = new User(); user.setUsername("刘悦"); user.setSex("1"); user.setBirthday(new Date()); user.setAddress("美国"); session.insert("test.insertUserAndGetKey",user); System.out.println("主键:"+user.getId()); session.commit(); session.close(); }
获取非自增主键:
userMapper.xml添加如下内容:
<!-- 添加用户,并返回刚刚插入数据对应的非增主键--> <insert id="insertUserAndGetKey2" parameterType="mybaties.pojo.User"> <!-- 使用mysql的uuid()生成主键执行过程: 首先通过uuid()得到主键,将主键设置到user对象的id属性中 其次在insert执行时,从user对象中取出id属性值 --> <selectKey keyProperty="id" resultType="java.lang.Integer" order="BEFORE"> SELECT UUID() </selectKey> INSERT INTO USER (ID,USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{id},#{username},#{birthday},#{sex},#{address}) </insert>
补充:获取ORCAL序列主键
userMapper.xml添加如下内容:
<!-- 添加用户,并返回刚刚插入数据对应的非增主键--> <insert id="insertUserAndGetKey2" parameterType="mybaties.pojo.User"> <selectKey keyProperty="id" resultType="java.lang.Integer" order="BEFORE"> SELECT 序列名.nextval() </selectKey> INSERT INTO USER (ID,USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{id},#{username},#{birthday},#{sex},#{address}) </insert>
7.删除一条数据
userMapper.xml添加如下内容:
<!--删除用户--> <!-- 根据ID删除用户--> <delete id="deleteUser" parameterType="java.lang.Integer"> DELETE FROM USER WHERE ID=#{id} </delete>
测试代码:
// 删除用户信息 @Test public void deleteUser(){ SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法 session.delete("test.deleteUser",6); session.commit(); session.close(); }
8.更新数据
userMapper.xml添加如下内容:
<!-- 根据id更新用户 分析: 需要传入用户的id 需要传入用户的更新信息 parameterType指定user对象,包括id和更新信息,注意:id必须存在 #{id}:从输入user对象中获取id属性值 --> <update id="updateUser" parameterType="mybaties.pojo.User"> UPDATE USER SET username= #{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE ID=#{id} </update>
测试程序:
// 更新用户信息 @Test public void updateUser(){ SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法 User user = new User(); user.setId(5); user.setUsername("刘悦后"); user.setSex("1"); user.setBirthday(new Date()); user.setAddress("加拿大"); session.update("test.updateUser",user); System.out.println("主键:"+user.getId()); session.commit(); session.close(); }
9.mybatis和hibernate本质区别和应用场景
hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。
对sql语句进行优化、修改比较困难的。
应用场景:
适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。。
mybatis:专注是sgl本身,需要程序员自己编写sgl语句,sgl修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。
应用场景:
适用与需求变化较多的项目,比如:互联网项目。
企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择。
10.总结原始 dao开发问题
1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
2、调用sglsesion.方法时将statement的id硬编码了
3、调用sglsession方法时传入的变量,由于sqlsession.方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
11Mapper动态代理方式
11.1 开发规范
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
userMapper.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.xml文件中的namespace与mapper接口的类路径相同。--> <mapper namespace="mybaties.mapper.UserMapper"> <select id="findUserById" parameterType="int" resultType="mybaties.pojo.User"> SELECT * FROM USER WHERE ID=#{id} </select> </mapper>
UserMapper.java:
public interface UserMapper { /** * 2、 接口方法名和Mapper.xml中定义的每个statement的id相同 * 3、 接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同 * 4、 接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同 */ public User findUserById(int id) throws Exception; }
selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
mapper接口方法参数只能有一个是否影响系统开发
系统框架中,dao层的代码是被业务层公用的。
即使mapper接口只有一个参数,可以使用包装类型的poio满足不同的业务方法的需求。
注意:持久层方法的参数可以包装类型、map...,service方法中建议不要使用包装类型(不利于业务层的可扩展,例如你的service方法参数是map别人调用service对应的方法时根本不知道你map里面有多少个key,或者key都是什么)。
11SqlMapConfig.xml配置文件
11.1配置内容
Mybaties全局配置文件:SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
11.2 properties(属性)重点
需求:
将数据库连接参数单独配置在db.propeties.中,只需要在SqlMapconfig.xml中加载db.properties.的属性值。
在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。
将数据库连接参数只配置在db.eroperties.中,原因:方便对参数进行统一管理,其它xml 可以引用该db.properties。
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> <!--加载属性文件:properties标签--> <properties resource="db.properties"> <!--注意!!!properties标签内部还可以配置一下标签和属性--> <!-- <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>--> </properties> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <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="sqlmap/userMapper.xml"></mapper> <mapper resource="mapper/userMapper.xml"></mapper> </mappers> </configuration>
properties 特性:
注意:MyBatis 将按照下面的顺序来加载属性:
◆在properties元素体内定义的属性首先被读取。
然后会读取properties 元素中resource或url加载的属性,它会覆盖已读取的同名属性。
建议:
不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
在properties 文件中定义属性名要有一定的特殊性,如:xxxxx.xxxxx.xxxx
11.3settings(全局配置参数):
mybais框架在运行时可以调整一些运行参数。
比如:开启二级缓存、开启延迟加载。。
全局参数将会影响mybatis.的运行行为。
详细参见https://blog.csdn.net/ycxzuoxin/article/details/104843730
<?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> ... <settings> <setting name="" value=""/> </settings> ... </configuration>
11.4typeAliases(类型别名)重点
需求:
在mapper.xml中,定义很多的statement,statement 需要parameterType.指定输入参数的类型、需要resultType.指定输出结果的映射类型。
如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType.指定的类型定义一些别名,在mappe.xml中通过别名定义,方便开发。
mybatis支持别名:
别名 |
映射的类型 |
_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 |
map |
Map |
自定义别名
<?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> ... <!-- 别名--> <typeAliases> <!-- 针对单个别名定义 type:类型的路径 alias:别名 --> <typeAlias type="mybaties.pojo.User" alias="user"/> <!-- 批量别名定义 指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以) --> <package name="mybaties.pojo"/> </typeAliases> ... </configuration>
使用别名:
<select id="findUserById" parameterType="int" resultType="user"> SELECT * FROM USER WHERE ID=#{id} </select>
11.4mappers(映射器)重点
Mapper配置的几种方法:
<mapper resource="" />
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
<mapper class="" />
使用mapper接口类路径
如:<mapper class="mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name=""/>
注册指定包下的所有mapper接口
如:<package name="mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
!-- 加载映射文件--> <mappers> <!--方法1:通过resource方法一次加载一个映射文件--> <!-- <mapper resource="sqlmap/userMapper.xml"></mapper>--> <mapper resource="mapper/UserMapper.xml"></mapper> <!--方法2:通过mapper接口加载单个映射文件 遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中 上边规范的前提是:使用的是mapper代理方法 --> <!-- <mapper class="mybaties.mapper.UserMapper"/>--> <!--方法3:批里加载mapper(推荐使用) 指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载 遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中 上边规范的前提是:使用的是mapper代理方法--> <!-- <package name="mybaties.mapper"/>--> </mappers>
12 输入映射和输出映射
Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。
parameterType(输入类型)
传递简单类型
参考上面内容。
传递pojo对象
Mybatis使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称。
传递pojo包装对象
开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
Pojo类中包含pojo。
需求:根据用户名查询用户信息,查询条件放到QueryVo的user属性中。
Uesr类扩展类:
/** * 用户扩展类 * 假如有一天,我们项目已经上线,但是我们想扩展一下User类,而我们有不能轻易改动User,我们可以通过实现类的方式对她进行扩展 */ public class UserExtension extends User { }
User类包装类:
/** * 用户包装类 */ public class UserQueryVo { // 在这里包装所有需要的查询条件:下面只包装了user对象(子类) private UserExtension userExtension; // ....还可以包装其他查询条件,如,订单商品,等 public UserExtension getUserExtension() { return userExtension; } public void setUserExtension(UserExtension userExtension) { this.userExtension = userExtension; } }
mapper.xml新增statement:
<!-- 综合查询,复杂查询条件 #{userExtension.sex}:取出用户包装类中的性别值 ${userExtension.username}:取出用户包装类中的用户名称 parameterType="mybaties.vo.USerQueryVo":传递进来的复杂参数都包含在USerQueryVo对象中 resultType="mybaties.extension.UserExtension":返回值都被添加到扩展过的User子类UserExtension中(因为查询出来的内容可能包括User属性之外的内容) --> <select id="findUserList" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension"> SELECT * FROM USER WHERE SEX=#{userExtension.sex} AND USERNAME LIKE'%${userExtension.username}%' </select>
mapper.java新增查询方法:
public List<UserExtension> findUserList(UserQueryVo queryVo) throws Exception;
测试代码:
@Test public void findUserList() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // mybaties自动生成mapper代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 创建用户类扩展类并赋值查询条件 UserExtension user = new UserExtension(); user.setSex("1"); user.setUsername("刘悦"); // 创建用户类包装类,并给userExtension属性赋值 UserQueryVo queryVo = new UserQueryVo(); queryVo.setUserExtension(user); List<UserExtension> userList = mapper.findUserList(queryVo); System.out.println(userList); }
传递HashMap
<!-- 传递HashMap参数 sex,username都是hashmap里面的key --> <select id="findUserListByMap" parameterType="hashMap" resultType="mybaties.extension.UserExtension"> SELECT * FROM USER WHERE SEX=#{sex} AND USERNAME LIKE'%${username}%' </select>
resultType(输出类型)
使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo中的属性名全部不一致,没有创建 poio对象。
只要查询出来的列名和pojo中的属性有一个一致,就会创建 pojo对象。
输出简单类型
<!--简单类型查询--> <select id="findUserListCount" resultType="int" parameterType="mybaties.vo.UserQueryVo"> SELECT COUNT(*) FROM USER WHERE SEX=#{userExtension.sex} AND USERNAME LIKE'%${userExtension.username}%' </select>
输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。
输出pojo对象
参考上面内容
输出pojo列表
参考上面内容
resultMap
resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还需要将查询结果映射到pojo对象中。
resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
mapper.xml:
<!-- resoutMap--> <!--定义resultMap 将SELECT id id_,username username_FROM USER 和User类中的属性作一个映射关系 type:resultMap最终映射的java对象类型,可以使用别名 id:resultMap的唯一标识--> <resultMap id="userResultMap" type="mybaties.pojo.User"> <!-- <id />表示查询结果集中唯一标识,非常重要。如果是多个字段为复合唯一约束则定义多个<id /> column:查询出来的列名 property:type指定的pojo类型中的属性名最终resultMap对column和property作一个映射关系(对应关系) --> <id column="id_" property="id"></id> <!-- <result>:对普通名映射定义 column:查询出来的列名 property:type指定的pojo类型中的属性名最终resultMap对column和property作一个映射关系(对应关系) --> <result column="username_" property="username"></result> </resultMap> <!-- 使用resultMap进行输出映射 resultMap:指定定义的resultMap的id,如果这个resultMap在其它的mapper文件,(userResultMap)前边需要加namespace的值 --> <select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap"> SELECT id id_,username username FROM USER WHERE ID=#{id} </select>
mapper.java:
public User findUserByIdResultMap(int id) throws Exception;
测试:
@Test public void findUserByIdResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.findUserByIdResultMap(5); System.out.println(user); }
13 动态sql:
通过mybatis提供的各种标签方法实现动态拼接sql。
if
需求根据查询条件是否传入,动态改变SQL,以改造下面的SQL为例:
<select id="findUserList" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension"> SELECT * FROM USER WHERE SEX=#{userExtension.sex} AND USERNAME LIKE'%${userExtension.username}%' </select>
<!--上面的statement改为动态SQL--> <select id="findUserListWhitDynamicSql" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension"> SELECT * FROM USER <!--where标签会去掉第一个查询条件前面的and--> <where> <if test="userExtension!=null"> <if test="userExtension.sex!=null and userExtension.sex!=''"> and SEX=#{userExtension.sex} </if> <if test="userExtension.username!=null and userExtension.username!=''"> and USERNAME LIKE'%${userExtension.username}%' </if> </if> </where> </select>
测试:
//复杂参数综合查询(动态SQL) @Test public void findUserListWhitDynamicSql() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); // mybaties自动生成mapper代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 创建用户类扩展类并赋值查询条件 UserExtension user = new UserExtension(); // 由于是动态sql,某个值如果不设置,对应的查询条件就不会拼接到SQL上 // user.setSex("1"); // user.setUsername("刘悦"); // 创建用户类包装类,并给userExtension属性赋值 UserQueryVo queryVo = new UserQueryVo(); queryVo.setUserExtension(user); List<UserExtension> userList = mapper.findUserListWhitDynamicSql(null); System.out.println(userList); }
Sql片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:
定义SQL片段:
<!-- 定义SQL片段 id:SQL片段的唯一标识 经验:是基于单表来定义SQL片段,这样话这个SQL片段可重用性才高 在SQL片段中不要包括where --> <sql id="userWhere" > <if test="userExtension!=null"> <if test="userExtension.sex!=null and userExtension.sex!=''"> and SEX=#{userExtension.sex} </if> <if test="userExtension.username!=null and userExtension.username!=''"> and USERNAME LIKE'%${userExtension.username}%' </if> </if> </sql>
引用SQL片段
<!-- 引用Sql片段--> <select id="findUserListWhitDynamicSql" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension"> SELECT * FROM USER <where> <!--如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace--> <include refid="userWhere"></include> </where> </select>
foreach
需求:
在用户查询列表和查询总数的statement中增加多个id输入查询
sl语句如下:
两种方法:
SELECT * FROM USER WHERE id=1 OR id=3 OR id=5
SELECT*FROM USER WHERE id IN(1,3,5)
uesr包装类添加属性:
mapper.xml:
<sql id="userWhere" > <if test="userExtension!=null"> <if test="userExtension.sex!=null and userExtension.sex!=''"> and SEX=#{userExtension.sex} </if> <if test="userExtension.username!=null and userExtension.username!=''"> and USERNAME LIKE'%${userExtension.username}%' </if> </if> <if test="arrayList!=null"> <!--如果传入的存放id的list不为null,就使用foreach遍历该arrayList collection:指定输入对象中集合属性名 item:每个追历生成对象中 open:开始遍历时拼接的串 close:结束遍历时拼擦的串 separator:遍历的两个对象中需要拼接的串 --> <!-- 最终实现下边的sql拼接: AND(id=1 OR id=10 OR id=16) --> <foreach collection="arrayList" item="list_each_id" open="AND(" separator="OR" close=")"> ID=#{list_each_id} </foreach> <!--SELECT * FROM USER WHERE SEX=? AND ID IN( ? , ? , ? ) 拼接方法如下--> <!-- <foreach collection="arrayList" item="list_each_id" open="AND ID IN(" separator="," close=")">--> <!-- #{list_each_id}--> <!-- </foreach>--> </if> </sql> <!-- foreach:传入list参数测试--> <select id="findUserListWhitMuchId" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension"> SELECT * FROM USER <!--where标签会去掉第一个查询条件前面的and--> <where> <!--如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace--> <include refid="userWhere"></include> </where> </select>
测试:
//foreach(传入list参数测试) @Test public void findUserListWhitMuchId() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); UserExtension user = new UserExtension(); user.setSex("1"); // user.setUsername("刘悦"); UserQueryVo queryVo = new UserQueryVo(); ArrayList<Integer> integers = new ArrayList<>(); integers.add(1); integers.add(3); integers.add(5); queryVo.setArrayList(integers); queryVo.setUserExtension(user); List<UserExtension> userList = mapper.findUserListWhitMuchId(queryVo); System.out.println(userList); }