mybatis(二)
传统dao执行数据库方法
public class TestMyBatis {
@Test
public void testSelectStduents(){
StudentDao student=new StudentDaoImpl();
List<Student> students = student.selectStudents();
students.forEach(student1 -> System.out.println(student1));
}
@Test
public void TestInsertStudent(){
StudentDao dao=new StudentDaoImpl();
Student student=new Student();
student.setId(1004);
student.setName("ccl");
student.setEmail("ccl@qq.com");
student.setAge(20);
dao.insertStudent(student);
}
}
List<Student>
students = student.selectStudents(); 调用
1、dao对象,类型是StudentDao,全限定名称是:com.hr.qjb.dao.StudentDao
全限定名称,和 namespace是一样的
2、方法名称,selectStudents,这个方法就是mapper文件中的id值, selectStudents
3、通过dao中方法的返回值也可以确定MyBatis要调用的SqlSession的方法
如果返回值是List,调用的是SqlSession.selectList()方法
如果返回值是int , 或是非List的,看mapper文件中的标签是<insert>
,<update>
就会调用
sqlSession的insert、update等方法
mybatis的动态代理:mybatis根据dao的方法调用,获取执行sql语句的信息
mybatis根据你的dao接口,创建出一个dao接口的实现类,并创建这个类的对象。
完成SqlSession调用方法,访问数据库。
动态代理getMapper
使用SqlSession.getMapper(dao接口.class) 获取这个dao接口的对象
public class TestMyBatis {
@Test
public void testSelectStduents(){
/*
* 使用mybatis的动态代理机制,使用SqlSession.getMapper(dao接口)
* getMapper 能获取dao接口对于的实现类对象
*
* */
SqlSession sqlSession= MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//调用dao的方法,执行数据库的操作
List<Student> students = dao.selectStudents();
students.forEach(student -> System.out.println(student));
}
传统dao执行数据库方法,需要接口的实现类支撑,但是使用动态代理以后,可以通过getMapper去代替接口实现类
通过执行System.out.println(dao.getClass().getName());查看dao的类型发现
com.sun.proxy.$Proxy2
可以看到用的就是动态代理
深入理解参数
传入一个简单类型参数
如果想要在进行数据库操作的时候加入参数
传入参数:从Java代码中把数据传入到mapper文件的sql语句中。
1、parameterType:写在mapper文件中的要给属性。表示dao接口中方法的参数的数据类型,
例如StudentDao接口
public Student selectStudent ById(Integer id)
parameterType:用来表示dao接口方法参数的数据类型
parameterType它的值是Java的数据类型全限定名称或者是mybatis定义的别名
例如:parameterType=“java.lang.Integer”
parameterType=“int”
注意:parameterType不是强制的,mybatis通过反射能够发现接口参数的数类型
所以可以没有。一般我们不写
在mapper文件(dao接口)获取简单类型的一个参数的值,使用#{任意字符}
例如
mapper类
<select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student where id=#{id}
</select>
测试类
public void testSelectStduents(){
SqlSession sqlSession= MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//传过去参数
Student student = dao.selectStudentById(1004);
System.out.println(student);
}
接口类
public interface StudentDao {
public Student selectStudentById(Integer id);
}
使用#{}之后,mybatis执行sql是使用jdbc中的PreparedStatement对象
由mybatis执行下面的代码:
1、mybatis创建Connection,PreparedStatement对象
String sql=”select id, name ,email,age from student where id=?“
PreparedStatement pst = conn.preparedStatement(sql);
pst.setInt(1,1001);
2、执行sql封装为resultType=”com.hr.qjb.domain.Student“这个对象
ResultSet rs = ps.executeQuery();
Student student = null;
while(rs.next()){
//从数据库取表的一行数据,存到一个Java对象属性中
Student student = new Student();
student.setId(rs.getInt(“id”));
student.setName(rs.getString(“name”));
student.setEmail(rs.getString(“email”));
student.setAge(rs.getString(“age”));
}
return student; // 给了dao方法调用的返回值
传入多个参数
使用@Param
当Dao接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名”),
mapper文件使用#{自定义参数名}
例如定义 List<Student>
selectStudent(@Param(“personName”) String name ){ … }
mapper 文件 select * from student where name = #{ personName}
接口方法:
List<Student>
selectMultiParam(@Param(“personName”) String name ,@Param(“personAge”) int age);
mapper文件:
<select id ="selectMultiParam" resultType="com.hr.qjb.domain.Student">
select id , name, email,age from student where name = #{personName} or age = #{personAge}
</select>
实例代码
StudentDao
//多个参数:命名参数,在形参定义的前面加入@Param("自定义参数名称")
List<Student> selectMultiParam(@Param("myname") String name ,
@Param("myage") Integer age);
StudentDao.xml
<!-- 多个参数,使用@Param命名-->
<select id="selectMultiParam" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student where name=#{myname} or age=#{myage}
</select>
测试类
@Test
public void testSelectMultiParam(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> students = dao.selectMultiParam("ccl", 20);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
使用对象
可以新建一个类,也可以使用已有的类,只要xml文件中#{}花括号中的参数与实体类的属性名对应就可以
studentDao.xml文件中:
多个参数,使用Java对象的属性值,作为参数实际值
使用对象的语法:#{属性名,javaType=类型名称,jdbcType=数据类型} 很少用
JavaType:指Java中的属性数据类型。
jdbcType:在数据库中的数据类型
例如:#{paramName,javaType=java.lang.String,jdbcType=VARCHAR}
<select id="selectMultiObject" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student where
name=#{paramName,javaType=java.lang.String,jdbcType=VARCHAR}
or age=#{paramAge,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>
上面是最为完整的,我们一般用简化的
简化方式:#{属性名} , JavaType,jdbcType的值mybatis反射能获取,不同提供
<select id="selectMultiObject" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student where
name=#{paramName} or age=#{paramAge}
</select>
vo:value object,放一些存储数据的类。比如说 提交请求参数, name, age现在想把name,age 传给一个service类。
vo:view object,从servlet把数据返回给浏览器使用的类,表示显示结果的类。
pojo:普通的有set,get方法的Java类,普通的Java对象
entity(domain域):实体类,和数据库中的表对应的类
使用位置(了解)
使用位置的话是通过接口中方法传递过来的参数从左往右计算,从零开始数,
例如:
List<Student> selectMultiPosition(String name , Integer age);
想要进行查询的话就是
<select id="selectMultiPosition" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student where
name=#{arg0} or age=#{arg1}
</select>
在mybatis 3.4之前 , 使用#{0} ,#{1} 表示
在mybatis3.4之后,使用#{arg0} , #{arg1}
使用map(了解)
List<Student> selectMultiMap(Map<String,Object> map);
使用Map,使用语法#{map的key}
xml
<select id="selectMultiMap" resultType="com.hr.qjb.domain.Student">
select id , name , email ,age from student where name=#{name} or age=#{age}
</select>
测试
@Test
public void testSelectMultiMap(){
//获取SqlSession对象
SqlSession sqlSession= MyBatisUtils.getSqlSession();
//通过getMapper方法,得到dao对象
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//因为要传一个map,所以创建要给map
Map<String ,Object> map =new HashMap<>();
map.put("name","ccl");
map.put("age",20);
//通过传过去的map调用方法,得到结果集,并且打印
List<Student> students = dao.selectMultiMap(map);
students.forEach(student -> System.out.println(student));
}
占位符
$和#的区别
select id , name ,email, age from student where id=#{id}
#的结果: select id , name ,email, age from student where id=?
select id , name ,email, age from student where id=${id}
$的结果:select id , name ,email, age from student where id=1004
String sql="select id , name ,email, age from student where id = "+“1004”;
使用Statement对象执行sql,效率比PreparedStatement低。
1、#使用?在sql语句中能做站位的,使用PreparedStatement执行sql,效率高
2、#能够避免sql注入,更安全
3、$不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
4、$有sql注入的风险,缺乏安全性
5、$:可以替换表名或者列名
#:占位符,告诉mybatis使用实际的参数值代替。并使用PrepareStatement对象执行sql语句。#{…}代替sql语句的"?"。这样做更安全,更迅速,通常也是首选做法,
mappper文件
<select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student where id=#{id}
</select>
转为MyBatis的执行是:
String sql="select id , name ,email , age from student where id=?";
PreparedStatement ps= conn.prepareStatement(sql);
ps.setInt(1,1005);
解释:
where id = ? 就是where id = #{id}
ps.setInt(1,1005) , 1005 会替换掉 #{id}
$
$ 字符串替换,告诉mybatis 使用 包 含 的 “ 字 符 串 ” 替 换 所 在 位 置 。 使 用 S t a t e m e n t 把 s q l 语 句 和 包含的“字符串” 替换所在位置。使用 Statement 把sql 语句和 包含的“字符串”替换所在位置。使用Statement把sql语句和{}的内容连接起来,主要用在替换表名,列名,不同列排序等操作。
: 可 以 替 换 表 名 或 者 列 名 , 你 能 确 定 数 据 是 安 全 的 , 可 以 使 用 :可以替换表名或者列名,你能确定数据是安全的,可以使用 :可以替换表名或者列名,你能确定数据是安全的,可以使用
MyBatis输出结果
resultType
resultType:结果类型,指sql语句执行完毕后,数据转为的Java对象。|java 类型是任意的。
resultType结果类型它的值:1、类型的全限定名称, 2、类型的别名, 例如java.lang.Integer别名为int
处理方式:
1、mybatis执行sql语句,然后mybatis调用类的无参数构造方法,创建对象
2、mybatis把ResultSet指定列值赋给同名的属性。
<select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.hr.qjb.domain.Student">
select id , name ,email, age from student
</select>
对等的jdbc
ResultSet rs =executeQuery(" select id, name ,email ,age from student")
while (rs.next()){
Student student =new Student();
student.setId(rs.getInt(“id”));
student.setname(rs.getString(“name”));
student.setEamil(rs.getSring(eamil));
student.setInt(rs.getInt(“age”));
}
返回对象类型
<select id="selectStudentReturnViewStudent" resultType="com.hr.qjb.vo.ViewStudent">
select id ,name from student where id=#{sid}
</select>
不管查询多少列,他只会存储类中有的属性
返回简单类型
全限定名称
<select id="countStudent" resultType="java.lang.Integer">
select count(*) from student
</select>
或别名
<select id="countStudent" resultType="int">
select count(*) from student
</select>
定义自定义类型的别名
1、在mybatis主配置文件中定义,<typeAlias>
定义别名
2、可以在resultType中使用自定义别名
第一种方式:
可以指定一个类型 一个自定义别名
type:自定义类型的全限定名称
alias:别名(短小,容易记忆的)
例如
<typeAlias type="com.hr.qjb.vo.ViewStudent" alias="vstu"/>
<typeAlias type="com.hr.qjb.domain.Student" alias="stu"/>
alias的值就是类的别名
第二种方式
<package>
name是包名,这个包中的所有类,类名就是别名(类名不区分大小写)
<package name="com.hr.qjb.domain"/>
<package name="com.hr.qjb.vo"/>
定义路径下的类的类名就是每个类的别名
返回Map
1、列名是map的key,列值是value
2、只能最多返回一行记录,多余一行是错误
<select id="selectMapById" resultType="map">
select id, name from student where id=#{stuid}
</select>
resultMap
resultMap:结果映射,指定列名和Java对象的属性对应关系。
1、你自定义列值赋给哪个属性
2、当你的列名和属性名不一样时,一定使用resultMap
resultMap使用步骤
xml
使用resultMap
1、先定义resultMap
2、在select标签,使用resultMap来引用定义的
定义resultMap
id:是自定义的名称,表示你定义的这个resultMap
type:Java类型的全限定名称
<resultMap id="studentMap" type="com.hr.qjb.domain.Student">
主键列,使用id标签
column:列名
property:Java类型的属性名
<id column="id" property="id" />
非主键列,使用result
<result column="name" property="email"/>
<result column="email" property="name"/>
<result column="age" property="age"/>
如果遇到表中的列名跟类中的属性名不一致时,可以通过resultMap解决, column是表中列名,property是Java类种属性的名字
注意:resultMap和resultType不要一起用,二选一
这是解决列名和属性名不一样的第一种方式
第二种方式
resultType的默认原则是 同名的列值赋值给同名的属性,使用列别名(Java对象的属性名)解决
在sql语句上进行解决
原来:select id , name , email , age from student
为了保持跟Java类中的属性名一样,将sql语句改为
select id as stuid, name as stuname, email as stuemail ,age as stuage from student
解决!
Like模糊查询
第一个模糊查询,在Java代码中 指定like的内容
xml
<select id="selectLikeOne" resultType="com.hr.qjb.domain.Student">
select id, name , email , age from student where name like #{name}
</select>
在测试传值的时候将模糊查询的数据在Java代码中拼接好,传过去
@Test
public void testSelectLikeOne(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//准备好like的内容
String name ="%磊%";
List<Student> lists = dao.selectLikeOne(name);
lists.forEach(student -> System.out.println(student));
}
第二种方式:在mapper文件中来拼接like内容
xml
<select id="selectLikeTwo" resultType="com.hr.qjb.domain.Student">
select id , name ,email ,age from student where name like "%" #{name} "%"
</select>
在mapper文件中写好模糊数据的格式,只需要传一个要查的文字过来
@Test
public void testSelectLikeTwo(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List<Student> lists = dao.selectLikeTwo("磊");
lists.forEach(student -> System.out.println(student));
}
相比之下,第一个方法更加灵活,好理解,如果需要改模糊查询的方法,比如说要李姓:李%。等等再第一种方法里改Java代码的字符串即可,第二个问题要去改动mapper文件
动态sql
动态sql:sql的内容是变化的,可以根据条件获取到不同的sql语句
主要是where部分发生变化。
动态sql的实现,使用的是mybatis提供的标签,<if>
,<where>
,<foreach>
<if>
1、<if>
是判断条件的,
语法<if test="判断Java对象的属性值">
部分sql语句
</if>
<where>
2、<where>
用来包含 多个<if>
的,当多个if有一个成立的,<where>
会自动增加一个where关键字,并去掉if种多余的and,or等
<foreach>
3、<foreach>
循环Java中的数组,list集合的。主要用在sql的in语句中。
例如:学生id是 1001、1002、1003的三个学生
sql:select * from student where id in (1001,1002,1003);
语法规则
<foreach collection="" item="" open="" close="" separator="">
#{xxx}
</foreach>
collection:用来表示接口中方法参数的类型,如果是数组使用array ,如果是list 集合使用list
item:自定义的,表示数组和集合成员的变量
open:循环开始时的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
集合中是简单类型
<foreach collection="list" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
集合中是引用类型
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{stu.id}
</foreach>
separator、close、open只是为了拼接的话更加方便,也可以去掉,通过自己的思维,加上自己的拼接,完成要达到的目的
例如:去掉open、close,可以手动加载循环前后
<select id="selectForeachTwo" resultType="com.hr.qjb.domain.Student">
select * from student where id in (
<foreach collection="list" item="stu" separator=",">
#{stu.id}
</foreach>
)
</select>
这样拼接的话也可以
sql代码片段
sql代码片段,就是复用一些语法
步骤
1、先使用<sql id="自定义名称唯一">
sql语句,表名,字段等 </sql>
2、再使用,<include refid="id的值" />
复用
例如:
<sql id="studentSql" >
select id ,name ,email ,age from student
</sql>
将sql有可能复用的语句定义为id为studentSql
再用的话可以用
<select id="selectStudentWhere" resultType="com.hr.qjb.domain.Student">
<!--代码片段的调用-->
<include refid="studentSql"/>
<where>
<if test="name !=null and name !=''">
name=#{name}
</if>
<if test="age > 0">
and age > #{age}
</if>
</where>
</select>
mybatis.xml
<environments default="mydev">
<!--environment:一个数据库信息的配置,环境
id:一个唯一值,自定义,表示环境的名称-->
<environment id="mydev">
<!-- transactionManager:mybatis提交事务,回滚事务的方式
type:事务的处理的类型
1)JDBC:表示mybatis底层是调用JDBC中的Connection对象的,commit,rollback
2)MANAGED:把mybatis的事务处理委托给其他的容器(一个服务器软件,一个框架(spring))-->
<transactionManager type="JDBC"/>
<!-- dataSource:表示数据源,Java的体系中,规定实现了javax.sql.DataSource接口的都是数据源.
数据源表示Connection对象的
type:指定数据源的类型
1)POOLED:使用连接池,mybatis会创建PooledDataSource类
2)UPOOLED:不适用连接池,在每次执行sql语句,先创建连接,实行sql,在关闭连接
mybatis会创建一个UnPooledDataSource, 管理Connection对象的使用
3)JNDI:Java命名和目录服务(windows注册表)
-->
<dataSource type="POOLED">
<!--driver,user,username,password 是固定的,不能自定义-->
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
<!--访问数据库的用户名称-->
<property name="username" value="root"/>
<!--访问数据库的密码-->
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
属性配置文件
1、数据库的属性配置文件:把数据库的连接信息,放到单独文件中。和mybatis主配置文件分开。
目的是便于修改,保存,处理多个数据库的信息。
1、在resources目录中定义一个属性配置文件。 xxxx.properties , 例如 jdbc.propertes
在属性配置文件中,定义数据,格式是key=value
key:一般使用 做多级目录的。
例如 jdbc.mysql.driver , jdbc.driver ,
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql//…
jdbc.username=root
jdbc.password=123456
2、在mybatis的主配置文件,使用<property>
指定文件的位置
在需要使用值的地方,${key}
jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=123456
在mybatis.xml文件中需要加入读取jdbc.properties的语句
<properties resource="jdbc.properties"/>
mybatis.xml中使用package sql映射文件方法
<!-- 第一种方式:有一个,写一个语句,来指定位置-->
<mapper resource="com\hr\qjb\dao\StudentDao.xml"/>
<!-- <mapper resource="com\hr\qjb\dao\OrderDao.xml"/>-->
<!-- 第二种方式:指定包名
name:xml文件(mapper文件)所在的包名,这个包中所有的xml文件一次都加载给mybatis
使用package的要求
1)mapper文件名称需要和接口名称一样,区分大小写的一样
2)mapper文件和dao接口需要在同一目录下-->
<package name="com.hr.qjb.dao"/>
使用package的要求
1)mapper文件名称需要和接口名称一样,区分大小写的一样
2)mapper文件和dao接口需要在同一目录下
扩展
PageHelper分页
PageHelper做数据分页的
先在pom文件中导入依赖
<!-- PageHelper的依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
然后在mybatis.xml中配置插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
@Test
public void testSelectAll(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//加入PageHelper的方法,分页
//pageNum:表示显示第几页 ,第一页
//pageSize:表示一页有多少行数据
PageHelper.startPage(2,2);
List<Student> students = dao.selectAll();
students.forEach(student -> System.out.println(student));
}
主要使用PageHelper的startPage方法,第一个参数pageNum表示显示的页数,第二个参数pageSize:表示每页有多少条数据