1、原生态JDBC
1.1、原生态jdbc程序:
package lsq.mybatis.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 基本的jdbc程序
* @author Daniel Li
*
*/
public class JdbcTest {
public static void main(String[] args) {
//数据库连接
Connection connection = null;
//预编译的Statement,使用预编译的Statement提高数据库性能
PreparedStatement preparedStatement = null;
//结果集
ResultSet resultSet = null;
try {
//注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisday1", "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"));
}
} catch (Exception e) {
e.printStackTrace();
} finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
1.2、原生态jdbc问题总结
1)数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。
设想:使用数据库连接池管理数据库连接。
2)将sql语句硬编码到Java代码中,如果sql语句修改,需要重新编译Java代码,不利于系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,也不需要对java代码进行重新编译。
3)向preparedStatement中设置参数,对占位符位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。
4)从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。
2、Mybatis框架
2.1、Mybatis是什么?
Mybatis是一个持久层框架,是Apache下的顶级项目。
Mybatis让程序员将主要精力放在sql上,通过Mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要的sql语句。
Mybatis可以将向preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
2.3、Mybatis基础架构
基础支持层:Mybatis的一些运行环境(数据源、事务、基本配置加载、缓存处理等)
数据处理层:Mybatis负责接收参数、解析参数,并且执行返回映射结果集。
接口层:Mybatis提供操作数据库的一些接口,供程序员使用。
2.2、Mybatis框架运行流程
加载全局配置文件:加载数据源、事务,用来操作数据库;
获取sqlSessionFactory:用来生成sqlSession,其中sqlSessionFactory是线程安全的(不共享)
sqlSession:由sqlSessionFactory生产,sqlSession中提供了一系列操作数据库的接口,程序员通过调用sqlSession提供的接口来操作数据。
3、入门程序
3.1、创建全局配置文件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>
<!-- mybatis初始化环境的配置,可以配置多个环境 spring tools suit -->
<environments default="development">
<!-- 配置当前自己的运行环境 -->
<environment id="development">
<!-- jdbc事务,Mybatis负责管理 -->
<transactionManager type="JDBC" />
<!-- jdbc数据源,Mybatis进行管理 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis_day01?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlMap/UserMapper.xml" />
</mappers>
</configuration>
3.2、创建映射文件
<?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:表示mybatis的映射文件的开始,
namespace:命名空间,用来隔离sql语句(mapper文件的唯一标示),可以随意命名
注意:namespace在接口代理开发中,有特殊含义,不能自定义命名
-->
<mapper namespace="test">
<!-- select:mybatis提供的用来查询的标签;
id:mybatis加载映射文件之后,把映射文件封装成一个对象,mappedStatement;
查询语句被封装成一个Statement对象,id就是Statement的唯一标识;
resultType:返回值类型,返回集合,不管返回的是单个对象还是集合,都是一样的返回值类型
-->
<select id="findAllUser" resultType="lsq.study.domain.User">
select * from user
</select>
</mapper>
3.3、编写测试方法
package lsq.study.mybatis.test;
import java.io.InputStream;
import java.util.List;
import lsq.study.domain.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;
public class Main1 {
@Test
public void findAllUser() throws Exception{
//1、加载全局配置文件
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2、获取sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3、使用sqlSessionFactory获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//4、使用sqlSession操作数据库
List<User> list = sqlSession.selectList("test.findAllUser");
System.out.println(list);
}
}
3.4、根据用户id进行查询
创建映射文件
<!--
根据用户id进行查询
id:statement的唯一标识;
parameterType:传递参数类型
resultType:返回值类型,根据id查询,返回的是单个对象,和上边查询集合的写法一样
#{}:占位符,Mybatis会解析#{}为占位符。如果传递的参数是基本类型:int,string,long等等,#{}里面可以任意写。
-->
<select id="findUserById" parameterType="int" resultType="lsq.study.domain.User">
select * from user where id = #{id}
</select>
测试方法
@Test
public void findUserById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", 10);
System.out.println(user);
}
3.5、根据用户名进行模糊查询映射文件
<!--
根据用户名模糊查询
#{}:占位符,如果传递参数是非字符类型,获取参数是原样获取,如果是字符串,#{}获取时会自动添加一个引号。
${}:拼接sql语句,不管传递参数是什么类型,都是原样获取
如果传递是基本类型参数,int,string等,${}只能写value;
如果传递参数是对象,#{}${}都是ognl表达式来获取,对象导航语言 属性.属性.属性
-->
<select id="findUserByUsername" parameterType="string" resultType="lsq.study.domain.User">
select * from user where username like '%${value}%'
</select>
测试方法
@Test
public void findUserByUsername(){
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserByUsername", "张");
System.out.println(list);
}
3.6、根据用户id进行删除
映射文件
<!--
根据用户id进行删除
-->
<delete id="deleteUserById" parameterType="int">
delete from user where id = #{id}
</delete>
测试方法
@Test
public void deleteUserById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUserById", 26);
sqlSession.commit();
sqlSession.close();
}
3.7、根据用户id进行更新
映射文件
<!--
根据id更新用户
parameterType:传递对象参数,使用ognl表达式进行获取。
-->
<update id="updateUserById" parameterType="lsq.study.domain.User">
update user set username=#{username},sex=#{sex},birthday=#{birthday} where id=#{id}
</update>
测试方法
@Test
public void updateUserById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("上海市浦东新区成山路475弄45号401室");
user.setBirthday(new Date());
user.setUsername("马布里");
user.setSex("男");
user.setId(25);
sqlSession.update("test.updateUserById", user);
sqlSession.commit();
sqlSession.close();
}
3.8、添加用户
映射文件
<!--
添加用户
Mybatis返回主键:使用selectKey标签。
keyProperty,表示返回的对应JavaBean里边的属性(id);
order:由于MySQL自增主键是在insert语句执行之后生成的,所以要使用AFTER;
-->
<insert id="insertUser" parameterType="lsq.study.domain.User" useGeneratedKeys="true" keyProperty="id">
<!-- 第一种方式 -->
<!-- <selectKey keyProperty="id" order="AFTER" resultType="int">
select last_insert_id()
</selectKey> -->
<!-- 第二种方式返回主键:直接在insert标签中配置useGeneratedKeys/keyProperty -->
insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
测试代码
/*
* Hibernate中级联保存外键自动生成:
* user-order
* user.setOrders(orders)
* session.save(user)
*
* 但是在Mybatis中外键需要我们自己维护
*
*/
@Test
public void insertUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("上海市浦东新区成山路475弄45号401室");
user.setBirthday(new Date());
user.setUsername("steven");
user.setSex("女");
sqlSession.insert("test.insertUser", user);
System.out.println(user.getId()+"@@@@@@@@@@@@@@@@@");
sqlSession.commit();
sqlSession.close();
}
3.9、Mybatis返回主键
a、返回int类型自增主键
第一种方式:
第二种方式:
b、返回uuid主键
c、返回oracle
总结:
1、parameterType
基本类型
对象
2、ResultType
基本类型
集合、对象(都写对象类型)
3、#$
#{}:占位符,如果传递参数是基本类型,#{}括号中可以是任意字符。
如果传递的是pojo对象,使用ognl表达式进行获取:属性.属性
如果传递的是字符类型,#{}会自动添加引号;
如果传递的是非字符类型,#{}会原样获取。
${}:拼接sql语句,不管传递的是字符类型还是非字符类型都会原样获取。
如果传递的是基本类型,${}括号中只能写value。
4、selectList和selectOne
Mybatis提供的底层查询接口方法,理论上来说不能混合使用。在接口开发当中尤其不能进行混合使用。
4、Mybatis的dao开发
4.1、Mybatis的dao的普通开发模式
自己定义接口和接口实现类
接口代码:
package lsq.study.dao;
import java.util.List;
import lsq.study.domain.User;
public interface UserDao {
public List<User> findAllUser();
public User findUserById(Integer id);
public List<User> findUserByUsername(String username);
}
实现类:
package lsq.study.dao.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import lsq.study.dao.UserDao;
import lsq.study.domain.User;
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
String ns = "test.";
public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public List<User> findAllUser() {
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList(ns + "findAllUser");
return list;
}
@Override
public User findUserById(Integer id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne(ns + "findUserById", id);
return user;
}
@Override
public List<User> findUserByUsername(String username) {
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList(ns + "findUserByUsername", username);
return list;
}
}
测试方法
@Test
public void findAllUser01(){
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
List<User> list = userDao.findAllUser();
System.out.println(list);
}
@Test
public void findUserById01(){
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserById(25);
System.out.println(user);
}
@Test
public void findUserByUsername01(){
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
List<User> list = userDao.findUserByUsername("兰");
System.out.println(list);
}
4.2、Mybatis的接口代理开发模式
只需要编写接口即可:
package lsq.study.dao;
import java.util.List;
import lsq.study.domain.User;
public interface UserDao {
public List<User> findAllUser();
public User findUserById(Integer id);
public List<User> findUserByUsername(String username);
}
接口代码开发需要遵循几个规范:
☆:方法名必须和映射文件Statement的ID一致
☆:Namespace的名字必须是接口的全类路径名
☆:statement传递参数的类型必须和接口参数类型一致
☆:接口返回值类型必须和statement的返回值类型一致,Mybatis底层会根据接口返回值类型判断到底使用selectOne和selectList
注意:这里直接使用resource引用进行接口开发:
5、Mybatis和Hibernate的区别
Hibernate优点:Hibernate也是一个对JDBC封装的持久层框架。
☆:Hibernate是一个ORM框架,面向对象开发,不用自己写sql
☆:由于使用面向对象开发,如果进行数据库迁移,只需要修改方言
缺点:
☆:由于使用面向对象开发,不能开发特别复杂的业务项目。
☆:表的关系维护,Hibernate自己维护表关系,对我们是透明的,不容易进行优化。Hibernate表关系维护特别复杂。
☆:由于使用hql,先把hql转化成sql再进行执行,执行效率变低。生成的sql语句格式不容易维护。
适合需求基本不变的项目,企业当中的ERP、CRM
Mybatis框架:
Mybatis使用纯sql进行开发,程序员只需要集中精力在sql上面就可以,方便对sql进行优化。适合需求变化特别快的项目。互联网项目。
6、Mybatis全局配置文件sqlMapConfig
基本配置内容
SqlMapConfig.xml中的配置内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
6.1、properties
Mybatis资源引用标签,用来引入外部资源文件
6.2、typeAliases(别名):Mybatis提供的配置别名的标签
6.2.1 定义单个别名
添加了别名之后,映射文件中的变化
之前
之后
6.2.2 批量定义别名
该包下的所有类都会被定义别名,别名即为该类类名。
Mybatis默认支持别名
6.3、typeHandlers(类型处理器)
通常Mybatis的typeHandlers完成jdbc类型和java类型的转换。
通常情况下,Mybatis提供的类型处理器满足日常的需求,不需要自定义。
6.4、Mappers(映射器)
第一种引入映射文件
第二种引入映射文件
批量扫面接口
7、ParameterType(Mybatis输入参数)
基本类型、pojo、map7.1、传递POJO
☆:需求:
查询参数特别复杂,涉及非持久化的参数
查询用户性别为男,姓马用户
如果业务参数类型特别复杂,使用包装类型pojo进行传递参数
接口方法:
package lsq.study.dao;
import java.util.List;
import lsq.study.domain.QueryUserVo;
import lsq.study.domain.User;
public interface UserDao {
public List<User> findAllUser();
public User findUserById(Integer id);
public List<User> findUserByUsername(String username);
public List<User> findUserByUsernameAndSex(QueryUserVo vo);
}
映射文件编写:
测试方法
@Test
public void findUserByUsernameAndSex(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
QueryUserVo queryUserVo = new QueryUserVo();
UserCustom userCustom = new UserCustom();
userCustom.setSex("男");
userCustom.setUsername("马");
queryUserVo.setUserCustom(userCustom);
List<User> list = userDao.findUserByUsernameAndSex(queryUserVo);
System.out.println(list);
}
7.2、传递map
需求同上。
映射文件
接口方法
测试方法
@Test
public void findUserByMap(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("sex", "男");
map.put("username", "马");
List<User> list = userDao.findUserByMap(map);
System.out.println(list);
}
8、ResultType
基本类型、pojo(返回对象或者返回集合都是写全类路径名,或者别名)
8.1、pojo类型
☆:需求查询满足条件的总记录和查询满足条件的总记录数
查询性别为女并且姓兰
查询满足条件总记录数:
映射文件
接口方法:
测试方法
@Test
public void findUserByUsernameAndSex(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
QueryUserVo queryUserVo = new QueryUserVo();
UserCustom userCustom = new UserCustom();
userCustom.setSex("女");
userCustom.setUsername("兰");
queryUserVo.setUserCustom(userCustom);
List<User> list = userDao.findUserByUsernameAndSex(queryUserVo);
int count = userDao.findUserByUsernameAndSexCount(queryUserVo);
System.out.println(count + "@@@@@@@@@@@@@");
System.out.println(list);
}
8.2、map类型
开发中,如果返回的是基本类型,使用ResultType;如果返回的是对象,基本上使用ResultMap。
ResultMap高级映射:
映射文件:
测试方法
@Test
public void findAllUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> list = userDao.findAllUser();
System.out.println(list);
}
测试结果
9、动态sql
9.1、使用动态sql可以对sql语句进行灵活的封装、拼接。
<?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="lsq.study.dao.UserDao">
<resultMap type="user" id="BaseResultMapRm">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</resultMap>
<!--
where:Mybatis中提供的条件查询标签
if:Mybatis提供的条件判断标签
-->
<select id="findUserByUsernameAndSex" parameterType="queryUserVo" resultMap="BaseResultMapRm">
select * from user
<where>
<if test="userCustom.sex != null and userCustom.sex!=''">
and sex=#{userCustom.sex}
</if>
<if test="userCustom.username!=null and userCustom.username!=''">
and username like '%${userCustom.username}%'
</if>
</where>
</select>
<select id="findUserByUsernameAndSexCount" parameterType="queryUserVo" resultType="int">
select COUNT(1) from user
<where>
<if test="userCustom.sex != null and userCustom.sex!=''">
and sex=#{userCustom.sex}
</if>
<if test="userCustom.username!=null and userCustom.username!=''">
and username like '%${userCustom.username}%'
</if>
</where>
</select>
</mapper>
测试代码 @Test
public void findUserByUsernameAndSex(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
QueryUserVo queryUserVo = new QueryUserVo();
UserCustom userCustom = new UserCustom();
userCustom.setSex("男");
userCustom.setUsername("马");
queryUserVo.setUserCustom(userCustom);
List<User> list = userDao.findUserByUsernameAndSex(queryUserVo);
int count = userDao.findUserByUsernameAndSexCount(queryUserVo);
System.out.println(count+"############################");
System.out.println(list);
}
9.2、sql片段
<resultMap type="user" id="BaseResultMapRm">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="birthday" property="birthday" />
<result column="sex" property="sex" />
<result column="address" property="address" />
</resultMap>
<!--
定义sql片段,抽取公共的sql语句
sql:Mybatis提供的用来定义sql片段的标签;
id:sql片段的唯一标识,可以定义多个sql片段
-->
<sql id="query_sql">
<if test="userCustom.sex != null and userCustom.sex!=''">
and sex=#{userCustom.sex}
</if>
<if test="userCustom.username!=null and userCustom.username!=''">
and username like '%${userCustom.username}%'
</if>
</sql>
<!-- where:Mybatis中提供的条件查询标签 if:Mybatis提供的条件判断标签 -->
<select id="findUserByUsernameAndSex" parameterType="queryUserVo"
resultMap="BaseResultMapRm">
select * from user
<where>
<include refid="query_sql"></include>
</where>
</select>
<select id="findUserByUsernameAndSexCount" parameterType="queryUserVo"
resultType="int">
select COUNT(1) from user
<where>
<include refid="query_sql"></include>
</where>
</select>
9.3、ForeachSELECT * FROM USER WHERE id=1 OR id=23 OR id=33
SELECT * FROM USER WHERE id IN(1,23,33)
传递数组和集合都可以
映射文件测试代码:
@Test
public void findUserByIds(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(25);
ids.add(29);
QueryUserVo queryUserVo = new QueryUserVo();
queryUserVo.setIds(ids);
List<User> list = userDao.findUserByIds(queryUserVo);
System.out.println(list);
}
测试结果:测试代码
@Test
public void findUserByIdsIN(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(25);
ids.add(29);
QueryUserVo queryUserVo = new QueryUserVo();
queryUserVo.setIds(ids);
List<User> list = userDao.findUserByIdsIN(queryUserVo);
System.out.println(list);
}
测试结果: