添加依赖
maven中Mybatis需要添加2个依赖:mybatis依赖和数据库依赖(这里用mysql)
- 还看到一种使用mybatis+mybatis-spring的方法来使用mybatis,后者是spring与mybatis的整合包,提供了一些新的注解,详见链接
```xml
org.mybatis mybatis 3.5.2
<a name="ockzA"></a>
# xml版
<a name="C9QAh"></a>
## 编写MyBatis核心配置文件mybatis.xml
```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>
<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=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="1234t"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/rao/dao/Mapper.xml"/>
</mappers>
</configuration>
<environment>
为配置环境,可以有多个<environments default="?">
表示默认使用?这个配置环境。<transactionManager>
事务管理,默认使用JDBC<dataSource>
下为配置属性,参考JDBCdriver
为数据库驱动程序url
:数据库地址
?后面为参数 useSSL=true
表示安全连接 &
是转义字符,表示和
useUnicode=true
表示使用Unicode编码以支持中文characterEncoding=utf8
表示使用utf8字符编码username
password
然后是用户名和密码<mappers><mapper resource="com/rao/dao/Mapper.xml"/></mappers>``设置使用哪个Mapper.xml
注意xml路径表示是**包/xml**
而类是**包.类**
创建Dao(数据/set/get)
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
//构造,有参,无参
//set/get
//toString()
}
创建Dao-Mapper接口
- Mapper接口定义了操纵数据的一系列方法,这些方法各自对应一种SQL操作
**xxxMapper**
是官方推荐命名。usermapper相当于userdaopublic interface UserMapper { List<User> selectUser(); String selectPassword(String id); //id唯一姓名不一定唯一,所以用id查最好 void createUser(int id,String name,String pwd); ... }
创建Mapper.xml配置文件
用于将
sql语句
与对应的dao-mapper接口
联系在一起。可以看成dao-mapper.xml
就是dao-mapper接口
的实现类- idea不会编译src的java目录的xml文件 剧毒!解决方法见页面底。
- sql操作标签:
> ... <
写sql语句id
为对应的dao-mapper接口
的方法resultType
为返回值类型,- 可以是自定义类类型,基本类,基本类型,List类型… 详见链接 方法返回值为void时就不写resultType参数
- 单个map时:
Map<String, Object>key
时设置map, - 接收list时设置为T,
- 返回多个map时:
Map<String,Hotel>,resultType为Hotel
- 注意resultType与dao-Mapper接口的方法返回值是有关联的
- mybatis可以帮我们把对象序列化然后存储到数据库中,对象属性即是数据表的字段。(所以一个dao类就要新建一个mapper,这样就避免出现字段不一致的情况)
同样的从数据库中取一条数据,mybatis也会自动反序列化为对象
<?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接口所在的完整路径--> <mapper namespace="com.rao.testtemplate.mapper.StudentMapper"> <select id="selectUser" resultType="com.rao.dao.User"> select * from user </select> <insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="sid"> <foreach collection="list" item="item" index="index" separator=";"> insert into Student (sname,gender,classId) values(#{item.sname},#{item.gender},#{item.classId}) </foreach> </insert> </mapper>
- mapper接口传参如果是基本类且多个时,记得用@Param
- namespace为xml文件对应的mapper接口的完整路径
- id为sql对应的mapper方法名
- resultType,resultMap为返回类型。前者是返回一个,后者是返回多个。对于增删改操作可以不设置,默认返回操作记录数
- useGeneratedKeys为采用自增主键,keyProperty为主键名
Test
建立SQL连接
总之就一句话:sqlSessionFactory通过配置获取sqlSession(相当于数据库连接),然后sqlSession再拿Mapper对象然后执行
- 每一个mybatis程序都是以一个
SqlSessionFactory
实例为核心的 SqlSessionFactory
由SqlSessionFactoryBuilder
得到,SqlSessionFactory
是用于构建SqlSession
;SqlSession
完全包含了所有面向数据库执行SQL 语句的方法,即相当于原来JDBC里的Statement
加载配置文件创建sql连接:这三句是死的,来源于官方文档
String resource = "mybatis-config.xml"; //加载mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);//根据配置信息建立连接
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
以上读取配置文件比较麻烦,所以我们将其封装为工具类
,工具类我们以后可以直接拿来用
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
//static代码块的内容在类启动的一开始就被加载
static {
try {//加载流对象注意异常
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession(); //开启/建立连接
}
}
--------------------------------------------------------
优化sql获取连接,实现自动提交事务
public static SqlSession getSession(){
return getSession(true); //事务自动提交
}
public static SqlSession getSession(boolean flag){
return sqlSessionFactory.openSession(flag);
}
调用接口方法执行SQL操作
- 推荐第二种,第二种更加灵活
- 方法一: SqlSession直接通过Mapper的方法方法获取结果
- 方法1接收获取List不能直接接收List结果,需要通过
selectList()
- 方法1接收获取List不能直接接收List结果,需要通过
- 方法二:SqlSession绑定具体Mapper接口,由Mapper接口去调用自身的
sql操作方法
获取结果 - sqlSession用完记得关闭
public void test() {
//获取sqlSession连接
SqlSession session = MybatisUtil.getSession();
//执行sql语句
//方法一
List<User> users = session.selectList("com.rao.dao.UserMapper.selectUser");
//方法二
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){ //foreach循环输出List,快捷键:users.for+enter
System.out.println(user);
}
session.close();//关闭数据库连接
}
}
静态资源不编译的解决方法
如果是静态资源未编译,其他正常,会报没有找到Mapper.xml
idea工程目录的target下是编译后的目录,解决前会看到编译目录下没有xml文件,解决后出现xml文件
idea默认不编译src下的xml文件。在pom.xml中添加一个这个即可
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
导入约束问题
mapper.xml和核心配置文件mabits.xml中需要导入约束文件.dtd
所以最好复制已有的xml配置模板
注解版
mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
使用注解有一个xml功能不能替代,就是mybatis的映射功能,所以要保证字段与实体类同名
- 使用注解和配置文件协同开发,才是MyBatis的最佳实践!
- 利用注解开发的作用就是替代了mapper.xml映射文件,将sql的xml配置直接用注解形式写在对应的方法上(感觉更加直观一些)。而
**mybatis.xml**
还是推荐使用静态配置文件进行配置
sql注解主要分成 :
- @select ()
- @update ()
- @Insert ()
- @delete ()
注解版Dao-mapper接口
1、在我们的接口中添加注解
该代码块有点问题,有些方法接收user对象,占位符应该是user.? 可能user.可以省略? ```java @Select(“select id,name,pwd password from user”)//相当于select * from user; public ListgetAllUser();
//添加一个用户 @Insert(“insert into user (id,name,pwd) values (#{user.id},#{user.name},#{user.pwd})”) int addUser(User user); 2、测试 User user = new User(6, “秦疆”, “123456”); mapper.addUser(user); session.close();
//修改一个用户 @Update(“update user set name=#{name},pwd=#{pwd} where id = #{id}”) int updateUser(User user); 2、测试 @Test User user = new User(6, “秦疆”, “zxcvbn”); mapper.updateUser(user); session.close(); }
//根据id删除一个用户 @Delete(“delete from user where id = #{id}”) int deleteUser(@Param(“id”)int id); 2、测试 mapper.deleteUser(6); session.close();
**2、在mybatis的核心配置文件中注入(之前是注入xml文件)**<br />`<mappers>`<br />` <mapper class="com.kuang.mapper.UserMapper"/>`<br />`</mappers>`<br />**3、进行测试 还是跟以往一样**
```java
@Test
public void testGetAllUser() {
SqlSession session = MybatisUtils.getSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getAllUser();
for (User user : users){
System.out.println(user);
}
session.close();
}
@Select("SELECT * FROM users LIMIT #{offset}, #{maxResults}")
List<User> getAll(@Param("offset") int offset, @Param("maxResults") int maxResults);
#与$的区别
- #{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】
- 而${}是字符串替换,所以使用$替换时如果是字符串参数必须额外传入引号,否则语法错误,拼接出的sql字符串没有引号
- 任何时候都应该优先使用#{},$可能造成sql注入安全问题,另外$可能造成一些语法错误,如下
//注意不要错误使用,比如传入的需要预编译的sql已经被注入 String sql = “update ft_proposal set id = “+id; //id:3;drop table ft_proposal; PreparedStatement prepareStatement = conn.prepareStatement(sql); //这时已经sql注入了
- sql中的占位变量与方法参数同名时不加@Param可以。不同名时必须使用@Param
```sql
select * from ${tableName} where name = #{name}
假如表名是user; delete user; -- 则解析后变为
select * from user; delete user; -- where name = ?;
则--后认为是注释,则造成sql先执行了select * from user; 随后执行了delete user;
猜测:使用#{} 在sql编译期间就不会出现上述歧义,执行时才会进行替换 在sql看来 user; delete user; 是一个整体不会出现这种歧义
同时对于一些传入值特殊时#也会更有用,如传入"" $则编译为in() 报错 而#会编译为in(null) 不报错
#对于字符串会自动添加引号 而$不会,容易造成错误
线程安全问题
- SqlSession 接口有 3 个实现类:
- 1 DefaultSqlSession。非线程安全
- 2 SqlSessionManager 非线程安全
- 3 SqlSessionTemplate(线程安全,但是在 mybatis-spring 包中)
- 因此,SqlSession 不应该是单例的,于此同时,依赖于 session 的 Mapper 要么也应该是多例的,要么就要用上 ThreadLocal !
- 或者SqlSession的作用域应该仅仅为请求或者方法中。执行完请求/方法立刻关闭