java MyBatis 最轻量级 总结
求关注!!!
本人在 没啥事的时候, 看了一下 java MyBatis 然后总结出了一点点 相关的笔记, 有需要的拿去!!! 不需要的, 路过就行 ! ! !
一… 新建项目
不管你是 用 idea, 还是 用 其他的 java 编辑器, 都是 需要一个 maven 项目, 其他方式自己研究吧!
其他方式 都太麻烦了, java 用maven 还是很方便的, 就听我的吧!
pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.practice.MyBatis</groupId>
<artifactId>practice</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<properties>
<org.mybatis.version>3.4.6</org.mybatis.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${org.mybatis.version}</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>4.10</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
</project>
项目构建好了, 大概是长这个样子的, 具体步骤
去别人那里看看就会了
如果没出现, 可以看看右下键是不是有进度条, 进度条走满以后才算是项目构建完成, 然后就是 几个配置文件了 !
二… 基本配置
MyBatis配置文件 : 文件路径 项目名\src\main\resources\practiceMyBatis.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>
<!-- 自己写了一个拦截器, 用来拦截 sql日志中 ?(占位符) 转换成数据 用的
不用拦截器 结果
==> Preparing: UPDATE `user` SET name=?, sex=?, birthday=?, address=? WHERE `id` = ?
==> Parameters: set小明(String), 男(String), 2020-01-19(Date), 他家我不知道, 但是我用set语句更新了他(String), 1(Integer)
用拦截器 结果
==> Preparing: UPDATE `user` SET name='set小明', sex='男', birthday='2020-11-23 16:34:45', address='他家我不知道, 但是我用set语句更新了他' WHERE `id` = 1
-->
<plugins>
<plugin interceptor="cn.practice.MyBatis.dao.Interceptor.MybatisInterceptor">
</plugin>
</plugins>
<!-- 连接数据库的基本配置 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://网址:端口/数据库名?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!-- 声明 Userdao.xml 文件位置 \src\main\resources 往后写, 也可以放在 到 坐在的 包路径下 使用 package 去指定路径 -->
<mappers>
<mapper resource="mapper_xml/UserDao/Userdao.xml"/>
<mapper resource="mapper_xml/UserDao/UserdaoMapper.xml"/>
</mappers>
</configuration>
拦截器: 文件路径 cn.practice.MyBatis.dao.Interceptor.MybatisInterceptor
package cn.practice.MyBatis.dao.Interceptor;
import cn.practice.MyBatis.main.MyBatis_main;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.log4j.Logger;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;
@Intercepts ({
@Signature (type = Executor. class , method = "update" , args = {
MappedStatement. class , Object. class }),
@Signature (type = Executor. class , method = "query" , args = {
MappedStatement. class , Object. class , RowBounds. class ,
ResultHandler. class }) })
@SuppressWarnings ({ "unchecked" , "rawtypes" })
public class MybatisInterceptor implements Interceptor {
Logger logger = Logger.getLogger(MyBatis_main.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[ 0 ]; // 获取xml中的一个select/update/insert/delete节点,主要描述的是一条SQL语句
Object parameter = null ;
// 获取参数,if语句成立,表示sql语句有参数,参数格式是map形式
boolean getArgsGreOne = invocation.getArgs().length > 1;
if (getArgsGreOne) {
parameter = invocation.getArgs()[ 1 ];
}
String sqlId = mappedStatement.getId(); // 获取到节点的id,即sql语句的id
// System.out.println( "sqlId = " + sqlId);
BoundSql boundSql = mappedStatement.getBoundSql(parameter); // BoundSql就是封装myBatis最终产生的sql类
Configuration configuration = mappedStatement.getConfiguration(); // 获取节点的配置
String sql = getSql(configuration, boundSql, sqlId); // 获取到最终的sql语句
logger.debug("==> sqlId: "+sqlId);
logger.debug("==> Preparing: "+sql);
} catch (Exception e){
logger.error(e.getMessage(), e);
}
return invocation.proceed(); // 执行完上面的任务后,不改变原有的sql执行过程
}
// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句
public static String getSql(Configuration configuration, BoundSql boundSql,String sqlId) {
String sql = showSql(configuration, boundSql);
StringBuilder str = new StringBuilder( 100 );
str.append(sql);
return str.toString();
}
/*<br> *如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理<br> */
private static String getParameterValue(Object obj) {
String value = null ;
if (obj instanceof String) {
value = "'" + obj.toString() + "'" ;
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format( new Date()) + "'" ;
} else {
if (obj != null ) {
value = obj.toString();
} else {
value = "" ;
}
}
return value;
}
// 进行?的替换
public static String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject(); // 获取参数
List<ParameterMapping> parameterMappings = boundSql
.getParameterMappings();
String sql = boundSql.getSql().replaceAll( "[\\s]+" , " " );
// sql语句中多个空格都用一个空格代替
if (CollectionUtils.isNotEmpty(parameterMappings) && parameterObject != null ) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); // 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换<br> // 如果根据parameterObject.getClass()可以找到对应的类型,则替换
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst( "\\?" , Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject); // MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst( "\\?" , Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName); // 该分支是动态sql
sql = sql.replaceFirst( "\\?" , Matcher.quoteReplacement(getParameterValue(obj)));
} else {sql=sql.replaceFirst( "\\?" , "缺失" );} //打印出缺失,提醒该参数缺失并防止错位
}
}
}
return sql;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this );
}
@Override
public void setProperties(Properties properties) {
}
}
log4j配置文件: 文件路径 项目名\src\main\resources\log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=trace, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
加载MyBatis工具: 文件路径 cn.practice.MyBatis.Util.MyBatisUtil.java
package cn.practice.MyBatis.Util;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
/*
*
* 创建 SQL Session 工厂, 保证每次获取的都是一个 session
*
*/
private static SqlSessionFactory factory;
private static ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();
static{
try{
InputStream is = Resources.getResourceAsStream("practiceMyBatis.xml");
factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
}catch (IOException e){
e.printStackTrace();
}
}
/**
* 获取SqlSession的方法
*/
public static SqlSession getSession(){
SqlSession session = tl.get();
if(session==null){
tl.set(factory.openSession());
}
return tl.get();
}
public static void closeSession(){
SqlSession session = tl.get();
if(session!=null){
session.close();
}
tl.set(null);
}
}
Mybatis和log4j 都配置好完了, 现在项目应该长这样.
映射数据库实体类: 文件路径 cn.practice.MyBatis.dao.User.java
package cn.practice.MyBatis.dao;
import java.sql.Date;
public class User {
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
// set add get method alt + ins 快捷键自动生成的
// constructor method
// toString method
}
注意 java类里的 username 和 数据库的 name 字段不一致! ! ! 后边我会写怎么配置
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=104 DEFAULT CHARSET=utf8;
有了上边的, 目录结构, 就可以各种去写程序对数据库的增删改查操作了
第一节 和 第二节 都是 铺垫, 后边的才是 正题
三… 单个 insert 和 多个 insert
先说insert, 数据表里添加一条数据
先写一个java 的接口, 里边增删改查都有, 不过不用自己去手动实现, 由MyBatis去实现此接口. 这一节就将 insertUser 和 insertUsers
/*
*
* @Param 是 batis 中的一个注解, 用来告诉实现类, 给他传递的参数是什么名字, 可以是 任何数据类型
* 实列:
* User findUserById(@Param("userId") Integer userId);
* User *methodname*(@Param("userId") Integer userId,@Param("username") String username);
*/
package cn.practice.MyBatis.dao.dao.interfaceImpl;
import cn.practice.MyBatis.dao.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
User findUserById(@Param("userId") Integer userId);
List<User> findUserByUser(@Param("userClass") User userClass);
List<User> findUserByIds(@Param("Ids") Integer[] Ids);
void updateUser(@Param("userClass") User userClass);
void deleteUser(@Param("userId") Integer userId);
void insertUser(@Param("user") User user);
void insertUsers(@Param("users") User[] users);
List<User> findUserByUserWhere(@Param("userClass") User userClass);
}
下一步: 写一个xml 文件, 用来给 MyBatis 来实现接口
UserdaoMapper配置文件: 文件路径 项目名\src\main\resources\mapper_xml\UserDao\UserdaoMapper.xml
<!--namespace 接口类的 类全名 -->
<mapper namespace="cn.practice.MyBatis.dao.dao.interfaceImpl.UserMapper">
<!--标签:<insert/update/delete/select> id 是 接口中的方法名称,
<select> : resultType 查询结果, 对应的是哪个类(写类全名)
resultMap 定义数据库中字段 对应 类中的属性
引用参数: #{user.id}
接口定义中 @Param("user") 告诉mapper 的 数据类型
定义与 引用 实列:
基础类型
方法声明 实列 methodmane(@Param("userId") Integer userId);
引用 实列 #{userId}
对象类型
方法声明 实列 insertUser(@Param("user") User user);
引用 实列 #{user.id}
如果是对象类型, 需要有 set 和 get 方法
foreach 我放在 后边解释了
-->
<insert id="insertUser">
INSERT INTO `user`(`id`, `name`, `birthday`, `sex`, `address`)
VALUES (#{user.id}, #{user.username}, #{user.birthday}, #{user.sex}, #{user.address})
</insert>
<insert id="insertUsers">
INSERT INTO `user`(`id`, `name`, `birthday`, `sex`, `address`)
VALUES
<foreach collection="users" item="user" index="index" open="" close="" separator=",">
(#{user.id}, #{user.username}, #{user.birthday}, #{user.sex}, #{user.address})
</foreach>
</insert>
</mapper>
有了接口, 有了实现mapper文件, 可以开始使用了, 写一个main方法, 用来测试使用了…
public static void main(String[] args) {
Logger logger = Logger.getLogger(MyBatis_main.class);
Random random = new Random();
SqlSession session = MyBatisUtil.getSession();
UserMapper usermapper= session.getMapper(UserMapper.class);
int user_inser_id = random.nextInt();
logger.trace("使用mapper 的 接口映射的 insert 用户数据 userID = ("+user_inser_id+")");
usermapper.insertUser(new User(user_inser_id,"张三","女",Date.valueOf("2020-01-11"),"他们家我不知道"));
logger.trace("将数据提交到 数据库 使用 session.commit(true);");
session.commit(true);// insert and update and delete 执行后, 必须提交才可以 保存在 数据库中, 不然数据库中是没有数据的
logger.trace("use <foreach> mapper class insert users ");
User[] users = new User[]{
new User(100, "小明1", "男", Date.valueOf("2020-01-11"), "他们家我不知道"),
new User(101, "小明2", "男", Date.valueOf("2020-01-12"), "他们家我不知道"),
new User(102, "小明3", "女", Date.valueOf("2020-01-13"), "他们家我不知道"),
new User(103, "小明4", "男", Date.valueOf("2020-01-14"), "他们家我不知道"),
};
usermapper.insertUsers(users);
logger.trace("将数据提交到 数据库 使用 session.commit(true);");
session.commit(true); // insert and update and delete 执行后, 必须提交才可以 保存在 数据库中, 不然数据库中是没有数据的
}
上面代码运行完成以后, 运行结果 是这的 数据库中已经有数据了(日志前边, 有log4j 的 日志信息)
四… select 单条件, select 多字段多条件,select 单字段多条件
还是使用 第三节 的接口, 里边增删改查都有, 不过不用自己去手动实现, 由MyBatis去实现此接口. 这一节就将 findUserById和 findUserByIds, findUserByUserWhere
<!--标签:<insert/update/delete/select> id 是 接口中的方法名称,
<select> : resultType 查询结果, 对应的是哪个类(写类全名)
resultMap 定义数据库中字段 对应 类中的属性
引用参数: #{user.id}
接口定义中 @Param("user") 告诉mapper 的 数据类型
定义与 引用 实列:
基础类型
方法声明 实列 methodmane(@Param("userId") Integer userId);
引用 实列 #{userId}
对象类型
方法声明 实列 insertUser(@Param("user") User user);
引用 实列 #{user.id}
但是 如果是对象类型, 需要有 set 和 get 方法
-->
<!--select 单条件-->
<select id="findUserById" resultType="cn.practice.MyBatis.dao.User" resultMap="User">
select * from user where id=#{userId}
</select>
<!--select 单字段多条件 使用 in 或者 not in 关键字-->
<select id="findUserByIds" resultType="cn.practice.MyBatis.dao.User" resultMap="User">
select * from user where id in
<foreach collection="Ids" item="id" index="index" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- select 多字段多条件 这样写 有缺陷, 当 条件不满足的时, 生成的sql语句 会 有问题 -->
<select id="findUserByUser" resultType="cn.practice.MyBatis.dao.User" resultMap="User">
select * from user where
<if test="userClass.id != null">
id == #{userClass.id}
</if>
<if test="userClass.username != null and userClass.username != ''">
and name like concat('%',#{userClass.username},'%')
</if>
<if test="userClass.sex != null and userClass.sex != ''">
and sex like concat('%',#{userClass.sex},'%')
</if>
<if test="userClass.birthday != null">
and birthday == #{userClass.birthday}
</if>
<if test="userClass.address != null and userClass.birthday != ''">
and address like concat('%',#{userClass.address},'%')
</if>
</select>
<!-- select 多字段多条件 这样写 有缺陷, 当 条件不满足的时,MyBatis 的 where 标签中会处理 开头的 and 或者 or 将他们自动去除 -->
<select id="findUserByUserWhere" resultType="cn.practice.MyBatis.dao.User" resultMap="User">
select * from user
<where>
<if test="userClass.id != null">
id = #{userClass.id}
</if>
<if test="userClass.username != null and userClass.username != ''">
and name like concat('%',#{userClass.username},'%')
</if>
<if test="userClass.sex != null and userClass.sex != ''">
and sex like concat('%',#{userClass.sex},'%')
</if>
<if test="userClass.birthday != null and userClass.birthday != ''">
and birthday == #{userClass.birthday}
</if>
<if test="userClass.address != null and userClass.birthday != ''">
and address like concat('%',#{userClass.address},'%')
</if>
</where>
</select>
编写 main 方法测试 接口的实现
public static void main(String[] args) {
Logger logger = Logger.getLogger(MyBatis_main.class);
Random random = new Random();
SqlSession session = MyBatisUtil.getSession();
Integer user_inser_id = 100;
UserMapper usermapper = session.getMapper(UserMapper.class);
logger.trace("使用 mapper 的 接口映射的查询 用户数据 userID = ("+user_inser_id+")");
User mapperuser2 = usermapper.findUserById(user_inser_id);
logger.trace("使用 mapper 的 接口映射的 查询 用户数据 userID = ("+user_inser_id+")");
logger.trace(mapperuser2);
logger.trace("use <if test >mapper class multiple condition select ");
List<User> multiple_condition_selct_res = usermapper.findUserByUser(new User(null, "小明", null, null, null));
for (User u:multiple_condition_selct_res ) {
System.out.println(u);
}
logger.trace("use <foreach> mapper class in select ");
List<User> userByIds = usermapper.findUserByIds(new Integer[]{1, 2, 3, 4, 5});
for (User u:userByIds ) {
System.out.println(u);
}
logger.trace("use <where> <if test > </where>mapper class multiple condition select ");
List<User> where_multiple_condition_selct_res = usermapper.findUserByUserWhere(new User(1, "小明", "男", null, null));
for (User u:where_multiple_condition_selct_res ) {
System.out.println(u);
}
}
五… update 和 delete
还是使用 第三节 的接口, 里边增删改查都有, 不过不用自己去手动实现, 由MyBatis去实现此接口. 这一节就将 findUserById和 findUserByIds, findUserByUserWhere
<!--标签:<insert/update/delete/select> id 是 接口中的方法名称,
<select> : resultType 查询结果, 对应的是哪个类(写类全名)
resultMap 定义数据库中字段 对应 类中的属性
引用参数: #{user.id}
接口定义中 @Param("user") 告诉mapper 的 数据类型
定义与 引用 实列:
基础类型
方法声明 实列 methodmane(@Param("userId") Integer userId);
引用 实列 #{userId}
对象类型
方法声明 实列 insertUser(@Param("user") User user);
引用 实列 #{user.id}
但是 如果是对象类型, 需要有 set 和 get 方法
-->
<update id="updateUser">
UPDATE `user`
<set>
<if test="userClass.username != null and userClass.username != ''">name=#{userClass.username},</if>
<if test="userClass.sex != null and userClass.sex != ''">sex=#{userClass.sex},</if>
<if test="userClass.birthday != null">birthday=#{userClass.birthday},</if>
<if test="userClass.address != null and userClass.address != ''">address=#{userClass.address},</if>
</set>
WHERE `id` = #{userClass.id}
</update>
<delete id="deleteUser">
delete from user where id=#{userId}
</delete>
编写 main 方法测试 接口的实现
public static void main(String[] args) {
Logger logger = Logger.getLogger(MyBatis_main.class);
Random random = new Random();
SqlSession session = MyBatisUtil.getSession();
Integer user_delete_id = -485352151;
UserMapper usermapper = session.getMapper(UserMapper.class);
logger.trace("use <set> <if test > </set>mapper class multiple condition updateUser ");
usermapper.updateUser(new User(101, "set小明", "男", Date.valueOf("2020-01-19"), "他家我不知道, 但是我用set语句更新了他"));
session.commit(true);
logger.trace("use mapper class delete user ");
usermapper.deleteUser(user_delete_id);
session.commit(true);
}