Mybatis
解决eclipse maven工程中
src/main/resources目录下创建的文件夹所显示样式不是文件夹,而是"包"图标样式的问题
https://www.cnblogs.com/tjudzj/p/7457108.html
1、什么是框架?
它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。
使用框架的好处:
框架封装了很多的细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。
2、三层架构
表现层:
是用于展示数据的
业务层:
是处理业务需求
持久层:
是和数据库交互的
3、持久层技术解决方案
JDBC技术:
Connection
PreparedStatement
ResultSet
Spring的JdbcTemplate:
Spring中对jdbc的简单封装
Apache的DBUtils:
它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装
以上这些都不是框架
JDBC是规范
Spring的JdbcTemplate和Apache的DBUtils都只是工具类
4、mybatis的概述
mybatis是一个持久层框架,用java编写的。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程
它使用了ORM思想实现了结果集的封装。
ORM:
Object Relational Mappging 对象关系映射
简单的说:
就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表。
user User
id userId
user_name userName
今天我们需要做到
实体类中的属性和数据库表的字段名称保持一致。
user user
id id
user_name user_name
-
Mybatis框架的工作流程
1.数据源------>connection------>prepastatemnet----->resultset(jdbc) 2.加载配置文件--->sqlSessionFactory------>sqlSession(excutor)------>statement mapper配置文件(xml格式):全局配置文件(数据源,映射文件,别名)+映射文件(sql语句)
-
在控制台打印日志
1. 导入jar 包 log4j-1.2.17.jar log4j-api-2.3.jar log4j-core-2.3.jar slf4j-api-1.7.22.jar slf4j-log4j12-1.7.22.jar 2.添加日志文件 log4j.properties # Global logging configuration log4j.rootLogger=DEBUG, stdout # MyBatis logging configuration... log4j.logger.org.mybatis.example.BlogMapper=TRACE # 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 注意: 要求jdk环境必须是1.7+
-
输入多个参数:
1.多个参数封装到一个对象里 2.多个参数封装到一个map里 3.将多个参数通过注解的方式传到xml中 @Param("变量a")------#{变量a} 映射文件UserMapping.xml <!-- 多个输入参数 --> <!-- 方法一:根据用户的姓名模糊查询并且根据用户的性别精确查询,${}是拼接字符串 --> <select id="getByNameAndSex" parameterType="com.offcn.bean.User" resultType="com.offcn.bean.User"> select * from user where username like "%"#{username}"%" and sex=#{sex} </select> <!-- 方法二:将用户的姓名和性别封装map里 --> <select id="getByNameAndSexbak" parameterType="map" resultType="com.offcn.bean.User"> <!-- uname,usex 自定义 map 中key值 --> select * from user where username like "%"#{uname}"%" and sex=#{} </select> <!-- 方法三:接口中的方法里有多个参数,通过注解传递 --> <select id="getByNameAndSexbak1" resultType="com.offcn.bean.User"> select * from user where username like "%"#{username}"%" and sex=#{usex} </select> Dao层接口UserDao public List<User> getByNameAndSex(User user); public List<User> getByNameAndSexbak(Map map); public List<User> getByNameAndSexbak1(@Param("username") String uname,@Param("usex")String sex);
5、mybatis的入门
mybatis的环境搭建
第一步:创建maven工程并导入坐标
* 在 pom.xml 文件中添加 Mybatis3.4.5 的坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
第二步:创建实体类和dao的接口
public class User implements Serializable {}
public interface IUserDao {
/**
* 查询所有用户
*
* @return
*
* @Select("select * from user")
*/
List<User> findAll();
/**
* 保存用户
*/
void saveUser(User user);
/**
* 更新用户
*
* @param user
*/
void updateUser(User user);
/**
* 通过id删除用户
*
* @param userId
* @return
*/
int deleteUser(Integer userId);
/**
* 通过id查询用户信息
*
* @param userId
* @return
*/
User findById(Integer userId);
/**
* 根据名称模糊查询
*
* @param username
* @return
*/
List<User> findByName(String username);
/**
* 查询总记录条数
*
* @return
*/
int findTotal();
}
第三步:创建Mybatis的主配置文件
properties(属性)
settings(全局配置参数)
typeAlias(别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)<了解>
plugins(插件)<了解>
environments(环境集合属性对象)<了解>
environment(环境子属性对象)<了解>
transactionManager(事务管理)<了解>
dataSource(数据源)<了解>
mappers(映射器)
SqlMapConifg.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">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 环境配置 -->
<environments default="mysql">
<!-- 配置mysql -->
<environment id="mysql">
<!-- 配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 映射配置文件是指每个dao独立的配置文件-->
<mappers>
<!-- 如果使用注解配置 使用class属性来指定被注解的dao全限定类名
<mapper class="com.demos.dao.IUserDao"/>
-->
<mapper resource="com/demos/dao/IUserDao.xml"/>
</mappers>
</configuration>
第四步:创建映射配置文件 IUserDao.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 namespace="com.demos.dao.IUserDao">
<!-- 配置查询所有 -->
<select id="findAll" resultType="com.demos.domain.User">
select * from user;
</select>
<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.demos.domain.User">
<!-- 配置保存时获取插入的 id
keyColumn:主键对应的列名
keyProperty:主键对应的属性名
resultType:主键对应的类型
order="after":在插入之后查询
last_insert_id():查询函数
注意: 不可单独写,必须写在 insert 中
将查询到的主键赋值到了输入参数的主键上
-->
<selectKey keyColumn="id" keyProperty="id" resultType="int">
select last_insert_id();
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 更新用户 -->
<update id="updateUser" parameterType="com.demos.domain.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
<!-- 通过id删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{uid}
</delete>
<!-- 通过id查询用户信息 -->
<select id="findById" parameterType="java.lang.Integer" resultType="com.demos.domain.User">
select * from user where id = #{uid}
</select>
<!-- 根据名称模糊查询-->
<select id="findByName" resultType="com.demos.domain.User" parameterType="String">
select * from user where username like #{username}
<!-- select * from user where username like "%"#{username}"%" -->
<!-- select * from user where username like '%${value}%' -->
<!-- 当参数为user类型时 -->
<!-- select * from user where username like '%${username}%' -->
</select>
<!-- 查询总记录条数 -->
<select id="findTotal" resultType="int">
select count(*) from user;
</select>
</mapper>
第五步:创建测试文件
public class MybatiesTest {
private InputStream in;
private SqlSession session;
private IUserDao userDao;
@Before
public void init() throws Exception {
// 第一步:读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第二步:创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 第三步:创建SqlSession
SqlSessionFactory factory = builder.build(in);
// 第四步:创建Dao接口的代理对象
session = factory.openSession();
// 第五步:执行dao中的方法
userDao = session.getMapper(IUserDao.class);
}
@After
public void destory() throws Exception {
//提交sql增删改语句
session.commit();
// 第七步:释放资源
session.close();
in.close();
}
@Test // 查询所有信息
public void testFindAll() {
// 6.使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
// 打开 Mysql 数据库发现并没有添加任何记录,原因是什么?
// 这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务
// 提交呢?
// 可以使用:session.commit();来实现事务提交。加入事务提交后的代码如下:
@Test // 插入信息
public void testSave() {
User user = new User();
user.setUsername("lilei");
user.setAddress("北京市顺义区");
user.setSex("男");
user.setBirthday(new Date());
System.out.println("保存操作之前:" + user);
// 5.执行保存方法
userDao.saveUser(user);
System.out.println("保存操作之后:" + user);
}
@Test // 更新信息
public void testUpdate() {
User user = new User();
user.setId(49);
user.setUsername("li");
user.setAddress("北京");
user.setSex("女");
user.setBirthday(new Date());
userDao.updateUser(user);
}
@Test // 删除用户
public void testDelete() {
userDao.deleteUser(48);
}
@Test // 通过id查找用户
public void testFindOne() {
User user = userDao.findById(42);
System.out.println(user);
}
@Test // 通过名字模糊查询用户
public void testFindByName() {
List<User> users = userDao.findByName("%王%");
for (User user : users) {
System.out.println(user);
}
}
@Test // 查询总记录数
public void testFindTotal() {
int count = userDao.findTotal();
System.out.println("总记录数:" + count);
}
-
环境搭建的注意事项:
第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致。 在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper 所以:IUserDao 和 IUserMapper是一样的 第二个:在idea中创建目录的时候,它和包是不一样的 包在创建时:com.itheima.dao它是三级结构 目录在创建时:com.itheima.dao是一级目录 第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同 第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名 第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名 当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类。 Mapper代理模式:开发人员在遵循mybatis提供的一些规范的前提下,mybatis帮我们自动生成dao接口的实现类。 规范: 1.映射文件中命名空间等于dao层接口的全路径 2.映射文件中sql语句的id是dao层接口里的方法名 3.映射文件中输入参数和输出参数的类型和dao层接口里的方法的参数和返回结果保持一致。 mybatis的入门案例 // 第一步:读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 第二步:创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // 第三步:创建SqlSession SqlSessionFactory factory = builder.build(in); // 第四步:创建Dao接口的代理对象 SqlSession session = factory.openSession(); // 第五步:执行dao中的方法 IUserDao userDao = session.getMapper(IUserDao.class); // 6.使用代理对象执行查询所有方法 List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } // 第七步:释放资源 session.close(); in.close(); 注意事项: 不要忘记在映射配置中告知mybatis要封装到哪个实体类中 配置的方式:指定实体类的全限定类名 mybatis基于注解的入门案例: 把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句 同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名 注意事项: 在使用基于注解的 Mybatis 配置时,请移除 xml 的映射配置(IUserDao.xml)。 明确: 我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。 不管使用XML还是注解配置。 但是Mybatis它是支持写dao实现类的。
注意:
-
parameterType 属性:
1. 基本类型可以直接写类型名称,也可以使用 包名.类名的方式,例如 :java.lang.String。 Alias MappedType _byte byte byte Byte 2. 实体类类型,目前我们只能使用全限定类名。 代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。 传递 pojo 包装对象 1. QueryVo.java public class QueryVo implements Serializable { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } } 2. 编写持久层接口IUserDao.java /** * 根据 QueryVo 中的条件查询用户 * * @param vo * @return */ List<User> findByVo(QueryVo vo); 3. 持久层接口的映射文件IUserDao.xml <!-- 根据用户名称模糊查询,参数变成一个 QueryVo 对象了 --> <select id="findByVo" resultType="com.demos.domain.User" parameterType="com.demos.domain.QueryVo"> select * from user where username like #{user.username}; </select> 4. 测试类 @Test // 使用pojo 包装对象查询信息 public void testFindByQueryVo() { QueryVo vo = new QueryVo(); User user = new User(); user.setUsername("%王%"); vo.setUser(user); List<User> users = userDao.findByVo(vo); for (User u : users) { System.out.println(u); } }
-
resultType 属性: 返回结果类型(int,String,对象,List,map)
1. 用于指定结果集的类型。它支持基本类型和实体类类型。 2. 它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须 使用全限定类名。 3. 实体类名称,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。 特殊情况: 实体类属性和数据库表的列名已经不一致 1. 使用别名查询 <!-- 配置查询所有操作 --> <select id="findAll" resultType="com.demos.domain.User"> select id as userId,username as userName,birthday as userBirthday, sex as userSex,address as userAddress from user </select> 2. resultMap 结果类型 1. resultMap 标签: * 可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。 * 在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。 2. 定义 resultMap <!-- 建立 User 实体和数据库表的对应关系 type 属性:指定实体类的全限定类名 id 属性:给定一个唯一标识,是给查询 select 标签引用用的。 --> <resultMap type="com.demos.domain.User" id="userMap"> <id column="id" property="userId"/> <result column="username" property="userName"/> <result column="sex" property="userSex"/> </resultMap> id 标签:用于指定主键字段 result 标签:用于指定非主键字段 column 属性:用于指定数据库列名 property 属性:用于指定实体类属性名称 3. 映射配置 <!-- 配置查询所有操作 --> <select id="findAll" resultMap="userMap"> select * from user </select>
-
sql 语句中使用#{}字符:
它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。 具体的数据是由#{}里面的内容决定的。 * #{}中内容的写法: 1: 数据类型是基本类型,此处可以随意写。 2: 我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称。 它用的是 ognl 表达式。 * OGNL表达式: Object Graphic Navigation Language 对象 图 导航 语言 它是通过对象的取值方法来获取数据。在写法上把get给省略了。 语法格式就是使用 #{对象.对象}的方式 比如:我们获取用户的名称 类中的写法:user.getUsername(); OGNL表达式写法:user.username mybatis中为什么能直接写username,而不用user.呢: 因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名 eg: #{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user.而直接写 username。 * 打开 Mysql 数据库发现并没有添加任何记录,原因是什么? 这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务 提交呢? 可以使用:session.commit();来实现事务提交。 * #{}与${}的区别 #{}表示一个占位符号 1. 通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换, 2. #{}可以有效防止 sql 注入。#{}可以接收简单类型值或 pojo 属性值。 如果parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。 ${}表示拼接 sql 串 1. 通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, 2. ${}可以接收简单类型值或 pojo 属性值, 如果 parameterType 传输单个 String 类型值,${}括号中只能是 value。 如果 parameterType 传输单个简单类型值,${}括号中可以自定义 如果 parameterType 传输自定义类型(User) ${}括号中是属性 '%${username}%' 总结: #{}等价于占位符?,输入参数是非自定义类型#{任意定义},输入参数是自定义类型#{自定义类型的属性名} ${}等价于拼接方式,输入参数是非自定义类型(String除外)#{任意定义},输入参数是自定义类型#{自定义类型的属性名},String类型作为输入参数${value}
-
SqlMapConfig.xml 中配置的内容和顺序
-properties(属性) --property -settings(全局配置参数) --setting -typeAliases(类型别名) --typeAliase --package -typeHandlers(类型处理器) -objectFactory(对象工厂) -plugins(插件) -environments(环境集合属性对象) --environment(环境子属性对象) ---transactionManager(事务管理) ---dataSource(数据源) -mappers(映射器) --mapper --package 1. properties(属性) * 第一种 <properties> <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/> <property name="jdbc.username" value="root"/> <property name="jdbc.password" value="1234"/> </properties> * 第二种 * 在 classpath(resources) 下定义 db.properties 文件 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/eesy jdbc.username=root jdbc.password=1234 * properties 标签配置 <!-- 配置连接数据库的信息 resource属性:用于指定 properties配置文件的位置,要求配置文件必须在类路径下 resource="jdbcConfig.properties" url 属性: URL: Uniform Resource Locator 统一资源定位符 http://localhost:8080/mystroe/CategoryServlet URL 协议 主机 端口 URI URI:Uniform Resource Identifier 统一资源标识符 /mystroe/CategoryServlet 它是可以在 web 应用中唯一定位一个资源的路径 --> <properties url= file:///D:/main/resources/jdbcConfig.properties"></properties> * dataSource 标签就变成了引用上面的配置 <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> * 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"> <!-- mybatis的主配置文件 --> <configuration> <!-- 配置properties--> <properties resource="jdbcConfig.properties"></properties> <!--使用typeAliases配置别名,它只能配置domain中类的别名 --> <typeAliases> <package name="com.demos.domain"></package> </typeAliases> <!--配置环境--> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务 --> <transactionManager type="JDBC"></transactionManager> <!--配置连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </dataSource> </environment> </environments> <!-- 告知 mybatis 映射配置的位置 映射配置文件是指每个dao独立的配置文件--> <mappers> <!-- 如果使用注解配置 使用class属性来指定被注解的dao全限定类名 <mapper class="com.demos.dao.IUserDao"/> <mapper resource="com/demos/dao/IUserDao.xml"/>--> <package name="com.demos.dao"/> </mappers> </configuration> 2. typeAliases(类型别名) <typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="com.demos.domain.User"/> <!-- 批量别名定义,扫描整个包下所有类,别名为类名(首字母大写或小写都可以) --> <package name="com.demos.domain"/> </typeAliases> 注意: type:实体类的全限定类名 alias:指定别名后,不再区分大小写 3. mappers(映射器) 1: <mapper resource=" " /> 使用相对于类路径的资源如: <mapper resource="com/demos/dao/IUserDao.xml" /> 2: <mapper class=" " /> 使用 mapper 接口类路径如: <mapper class="com.demos.dao.IUserDao"/> 此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。 3: <package name=""/> 注册指定包下的所有 mapper 接口如: <package name="com.demos.dao"/> 此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。 package标签用于指定dao接口的包,指定之后不需要在写mapper以及resources或class
6、自定义Mybatis的分析:
mybatis在使用代理dao的方式实现增删改查时做什么事呢?
只有两件事:
第一:创建代理对象
第二:在代理对象中调用selectList
自定义mybatis能通过入门案例看到类
class Resources
class SqlSessionFactoryBuilder
interface SqlSessionFactory
interface SqlSession