完整CRUD

import com.spiritmark.mybatis.pojo.User;
import java.util.List; public interface UserDao { /**
* 根据id查询用户信息
*
* @param id
* @return
*/
public User queryUserById(String id); /**
* 查询所有用户信息
*
* @return
*/
public List<User> queryUserAll(); /**
* 新增用户
*
* @param user
*/
public void insertUser(User user); /**
* 更新用户信息
*
* @param user
*/
public void updateUser(User user); /**
* 根据id删除用户信息
*
* @param id
*/
public void deleteUser(String id);
}

回顾上一章的内容续: (1)---->https://blog.csdn.net/qq_42897427/article/details/89095367#MyBatis_1116

UserDaoImpl

import com.spiritmark.mybatis.dao.UserDao;
import com.spiritmark.mybatis.pojo.User;
import org.apache.ibatis.session.SqlSession;
import java.util.List; public class UserDaoImpl implements UserDao {
public SqlSession sqlSession; public UserDaoImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
} @Override
public User queryUserById(String id) {
return this.sqlSession.selectOne("UserDao.queryUserById", id);
} @Override
public List<User> queryUserAll() {
return this.sqlSession.selectList("UserDao.queryUserAll");
} @Override
public void insertUser(User user) {
this.sqlSession.insert("UserDao.insertUser", user);
} @Override
public void updateUser(User user) {
this.sqlSession.update("UserDao.updateUser", user);
} @Override
public void deleteUser(String id) {
this.sqlSession.delete("UserDao.deleteUser", id);
} }

编写UserDao对应的UserDaoMapper.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:命名空间,随便写,一般保证命名空间唯一
对应UserDao的全限定类名
-->
<mapper namespace="UserDao">
<!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
-->
<!--<select id="queryUserById" resultType="com.spiritmark.mybatis.pojo.User">-->
<!--select * from tb_user where id = #{id}-->
<!--</select>--> <!--使用别名-->
<select id="queryUserById" resultType="com.spiritmark.mybatis.pojo.User">
select
tuser.id as id,
tuser.user_name as userName,
tuser.password as password,
tuser.name as name,
tuser.age as age,
tuser.birthday as birthday,
tuser.sex as sex,
tuser.created as created,
tuser.updated as updated
from
tb_user tuser
where tuser.id = #{id};
</select> <select id="queryUserAll" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user;
</select> <!--插入数据-->
<insert id="insertUser" parameterType="com.spiritmark.mybatis.pojo.User">
INSERT INTO tb_user (
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
now(),
now()
);
</insert> <update id="updateUser" parameterType="com.spiritmark.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update> <delete id="deleteUser">
delete from tb_user where id=#{id}
</delete>
</mapper>

在mybatis-config.xml中添加配置:

	<mappers>
<mapper resource="mappers/MyMapper.xml"/>
<mapper resource="mappers/UserDaoMapper.xml"/>
</mappers>

添加UserDao的测试用例

POM.xml中添加

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

按住Alt+Enter,选择create test





1.创建测试的JUnit4 和测试的类名 UserDaoTest

2.勾上setUp 选择每一个实现进行测试

编写UserDao的测试用例

import com.spiritmark.mybatis.dao.UserDao;
import com.spiritmark.mybatis.dao.impl.UserDaoImpl;
import com.spiritmark.mybatis.pojo.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.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List; public class UserDaoTest {
//提供成员变量
public UserDao userDao;
//创建SqlSession
public SqlSession sqlSession; @Before
public void setUp() throws Exception {
// mybatis-config.xml
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream is = Resources.getResourceAsStream(resource);
// 构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// 获取sqlSession
sqlSession = sqlSessionFactory.openSession();
this.userDao = new UserDaoImpl(sqlSession);
} @Test
public void queryUserById() throws Exception {
System.out.println(this.userDao.queryUserById("1"));
} @Test
public void queryUserAll() throws Exception {
List<User> userList = this.userDao.queryUserAll();
for (User user : userList) {
System.out.println(user);
}
} @Test
public void insertUser() throws Exception {
User user = new User();
user.setAge(16);
user.setBirthday(new Date("2001/09/02"));
user.setName("陈彬");
user.setPassword("2509919428");
user.setSex(1);
user.setUserName("evan");
this.userDao.insertUser(user); this.sqlSession.commit();
} @Test
public void updateUser() throws Exception {
User user = new User();
user.setBirthday(new Date());
user.setName("轩轩");
user.setPassword("ad70395eed609829");
user.setSex(1);
user.setUserName("evanjin");
user.setId("1");
this.userDao.updateUser(user);
this.sqlSession.commit();
} @Test
public void deleteUser() throws Exception {
this.userDao.deleteUser("4");
this.sqlSession.commit();
} }



大家可以对照一下目录结构 ,如果是小白建议跟着来

解决数据库字段名和实体类属性名不一致的问题

查询数据的时候,发现查不到userName的信息,

User{id=‘2’, userName=‘null’, password=‘123456’, name=‘陈彬’, age=22, sex=0, birthday=‘1993-09-05’, created=‘2018-06-30 18:22:28.0’, updated=‘2018-06-30 18:22:28.0’}

原因:数据库的字段名是user_name,POJO中的属性名字是userName

两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名。

解决方案1:在sql语句中使用别名:

<select id="queryUserById" resultType="com.spiritmark.mybatis.pojo.User">
select
tuser.id as id,
tuser.user_name as userName,
tuser.password as password,
tuser.name as name,
tuser.age as age,
tuser.birthday as birthday,
tuser.sex as sex,
tuser.created as created,
tuser.updated as updated
from
tb_user tuser
where tuser.id = #{id};
</select>

解决方案2: 参考后面的resultMap (等会讲)

解决方案3:参考驼峰匹配 — mybatis-config.xml 的时候

动态代理Mapper实现类

1.接口->实现类->mapper.xml

2.实现类中,使用mybatis的方式非常类似

3.xml中的sql statement 硬编码到java代码中。

思考:能否只写接口,不写实现类。只编写接口和Mapper.xml即可?

因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。因此mybatis提供了接口的动态代理。

使用动态代理改造CRUD

  • 修改测试用例的setUp方法

如果把人生比作一副心电图, 如果你什么困难都没遇到 ,一帆风顺,那就死了,我 们人生的旅程中会遇到很多困难,希望电脑前的你不要放弃 ! 加油 困难使你 更强大!

  • 执行queryUserAll()方法

  • 分析原因,在UserMapper.xml中配置接口的全路径

    mapper.xml namespace

    如果希望使用mybatis通过的动态代理的接口,就需要namespace中的值,和需要对应的Mapper(dao)接口的全路径一致。Mapper中Namespace的定义本身是没有限制的,只要不重复即可,但如果使用Mybatis的DAO接口动态代理,则namespace必须为DAO接口的全路径,例如:com.spiritmark.mybatis.dao.UserDao

<mapper namespace="com.spiritmark.mybatis.dao.UserDao">

完整的例子

创建UserMapper接口(对应原UserDao)

public interface UserMapper {

   /**
* 登录(直接使用注解指定传入参数名称)
* @param userName
* @param password
* @return
*/
public User login(@Param("userName") String userName, @Param("password") String password); /**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
* @param tableName
* @return
*/
public List<User> queryUserByTableName(@Param("tableName") String tableName); /**
* 根据Id查询用户信息
* @param id
* @return
*/
public User queryUserById(Long id); /**
* 查询所有用户信息
* @return
*/
public List<User> queryUserAll(); /**
* 新增用户信息
* @param user
*/
public void insertUser(User user); /**
* 根据id更新用户信息
* @param user
*/
public void updateUser(User user); /**
* 根据id删除用户信息
* @param id
*/
public void deleteUserById(Long id);
}

创建UserMapper.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:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.spiritmark.mybatis.dao.UserMapper">
<!--
1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
-->
<select id="queryUserByTableName" resultType="com.spiritmark.mybatis.pojo.User">
select * from ${tableName}
</select> <select id="login" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select> <!-- statement,内容:sql语句。
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<select id="queryUserById" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where id = #{id}
</select>
<select id="queryUserAll" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user
</select>
<!-- 新增的Statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo属性名
-->
<insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
parameterType="com.spiritmark.mybatis.pojo.User">
INSERT INTO tb_user (
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
null,
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
NOW(),
NOW()
);
</insert>
<!--
更新的statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<update id="updateUser" parameterType="com.spiritmark.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update>
<!--
删除的statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<delete id="deleteUserById" parameterType="java.lang.String">
delete from tb_user where id=#{id}
</delete>
</mapper>

全局配置文件mybatis-config.xml引入UserMapper.xml

<mappers>
<mapper resource="mappers/MyMapper.xml"/>
<mapper resource="mappers/UserDaoMapper.xml"/>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>

创建UserMapper测试用例

import com.spiritmark.mybatis.dao.UserMapper;
import com.spiritmark.mybatis.pojo.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.Before;
import org.junit.Test; import java.io.InputStream;
import java.util.Date;
import java.util.List; public class UserMapperTest { public UserMapper userMapper; @Before
public void setUp() throws Exception {
// 指定配置文件
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(true); // 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径
// 2. 映射文件的statement的id必须和mapper接口的方法名保持一致
// 3. Statement的resultType必须和mapper接口方法的返回类型一致
// 4. statement的parameterType必须和mapper接口方法的参数类型一致(不一定)
this.userMapper = sqlSession.getMapper(UserMapper.class);
} @Test
public void testQueryUserByTableName() {
List<User> userList = this.userMapper.queryUserByTableName("tb_user");
for (User user : userList) {
System.out.println(user);
}
} @Test
public void testLogin() {
System.out.println(this.userMapper.login("hj", "123456"));
} @Test
public void testQueryUserById() {
System.out.println(this.userMapper.queryUserById("1"));
} @Test
public void testQueryUserAll() {
List<User> userList = this.userMapper.queryUserAll();
for (User user : userList) {
System.out.println(user);
}
} @Test
public void testInsertUser() {
User user = new User();
user.setAge(20);
user.setBirthday(new Date());
user.setName("彬啊子");
user.setPassword("T796666");
user.setSex(2);
user.setUserName("Mr.WU");
this.userMapper.insertUser(user);
System.out.println(user.getId());
} @Test
public void testUpdateUser() {
User user = new User();
user.setBirthday(new Date());
user.setName("龙龙啊");
user.setPassword("T79666");
user.setSex(0);
user.setUserName("Jinjin");
user.setId("2");
this.userMapper.updateUser(user);
} @Test
public void testDeleteUserById() {
this.userMapper.deleteUserById("1");
}
}

目录结构:

动态代理总结

mybatis-config.xml详解

			mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序

properties属性读取外部资源

properties配置的属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:

<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>

然后其中的属性就可以在整个配置文件中被用来替换需要动态配置的属性值。比如:

<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。

属性也可以被传递到 SqlSessionFactoryBuilder.build()方法中。例如:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
// ... or ...
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);

如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

1)在 properties 元素体内指定的属性首先被读取。

2)然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。

3)最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。

因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。

settings设置

<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

测试:

没有开启驼峰匹配:

2018-07-01 13:57:56,486 [main] [com.spiritmark.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ?
2018-07-01 13:57:56,524 [main] [com.spiritmark.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 13:57:56,568 [main] [com.spiritmark.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='null', password='123456', name='陈彬', age=20, sex=2, birthday='2018-07-01', created='2019-04-0 13:36:09.0', updated='2019-04-0 13:36:09.0'}

开启驼峰匹配:

2018-07-01 13:58:40,599 [main] [com.spiritmark.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ?
2018-07-01 13:58:40,642 [main] [com.spiritmark.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 13:58:40,661 [main] [com.spiritmark.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='陈彬', age=20, sex=2, birthday='2019-04-0 13:36:09.0', created='2018-07-01 13:36:09.0', updated='2018-07-01 13:36:09.0'}

typeAliases

类型别名是为 Java 类型命名的一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

<typeAliases>
<typeAlias type="com.spiritmark.mybatis.pojo.User" alias="User"/>
</typeAliases>

缺点:每个pojo类都要去配置。

解决方案:使用扫描包,扫描指定包下的所有类,扫描之后的别名就是类名(不区分大小写),建议使用的时候和类名一致

<typeAliases>
<!--type:实体类的全路径。alias:别名,通常首字母大写-->
<!--<typeAlias type="com.spiritmark.mybatis.pojo.User" alias="User"/>-->
<package name="com.spiritmark.mybatis.pojo"/>
</typeAliases>

Mybatis已经为普通的 Java 类型内建了许多相应的类型别名。它们都是大小写不敏感的.

typeHandlers(类型处理器)

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。

plugins(插件)拦截器

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

现在一些MyBatis 插件比如PageHelper都是基于这个原理,有时为了监控sql执行效率,也可以使用插件机制

原理:

自定义拦截器:

// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
<?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:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="接口的限定类名">
<!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
-->
<!--和接口的id形成对应关系-->
<select id="selectUser" resultType="com.spiritmark.mybatis.User">
select * from tb_user where id = #{id}
</select>

修改成直接连接的,之后详解(MyBatis-Config.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>
<!-- 环境,可以配置多个,default:指定采用哪个环境 -->
<environments default="test">
<!-- id:唯一标识 -->
<environment id="test">
<!-- 事务管理器,JDBC类型的事务管理器 -->
<transactionManager type="JDBC" />
<!-- 数据源,池类型的数据源 -->
<dataSource type="POOLED">
<!--连接数据的库的信息-->
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/MyBatistwo" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<!--导入映射文件-->
<mapper resource="mappers/MyMapper.xml" />
</mappers>
</configuration>

配置:

		<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>

上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。

environments(环境)

MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;

尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。

虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离

(1) UNPOOLED

 这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。

UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

driver : 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)。

url :这是数据库的 JDBC URL 地址。

username : 登录数据库的用户名。

password :登录数据库的密码。

defaultTransactionIsolationLevel : 默认的连接事务隔离级别。

作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为“driver.”,例如:

driver.encoding=UTF8

这将通过DriverManager.getConnection(url,driverProperties)方法传递值为 UTF8 的 encoding 属性给数据库驱动。

(2) POOLED

 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。

除了上述提到 UNPOOLED 下的属性外,会有更多属性用来配置 POOLED 的数据源:

poolMaximumActiveConnections : 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10

poolMaximumIdleConnections :任意时间可能存在的空闲连接数。

poolMaximumCheckoutTime :在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)

poolTimeToWait :这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒).

poolPingQuery : 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。

poolPingEnabled : 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。poolPingConnectionsNotUsedFor : 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

(3) JNDI

这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:

initial_context : 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。

data_source : 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给初始上下文。比如:

env.encoding=UTF8

这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。

mappers

需要告诉 MyBatis 到哪里去找到 SQL 映射语句。即告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers> <!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 如果包接口和xml文件放同一个目录可以 使用包 -->

这里所谓的mapper接口路径。实际上就是dao的接口路径。在mybatis中,通常把dao的包叫做mapper。类名,也叫做mapper

1、定义一个接口。

2、在接口所在的包中定义mapper.xml,并且要求xml文件和interface的名称要相同。

3、在mybatis-config.xml 中通过class路径,引入mapper(注解方式)。要求

mapper.xml 中的名称空间是类的接口的全路径。

注解方式:

<mappers>
<mapper resource="mappers/MyMapper.xml"/>
<mapper resource="mappers/UserDaoMapper.xml"/>
<!--注解方式可以使用如下配置方式-->
<mapper class="com.spiritmark.mybatis.dao.UserMapper"/>
</mappers>

问题:1、mapper.xml 和 java文件没有分离。 之后的教程讲述和spring整合之后解决。

2、需要一个一个的去加载mapper。

当然也可以使用包扫描(必须使用注解方式,即在接口方法上使用注解,如@Select("select * from tb_user ")):

缺点:

1、如果包的路径有很多?

2、mapper.xml和mapper.java没有分离。

spring整合的时候解决。

Mapper XML文件详解

CRUD标签

1.select

select – 书写查询sql语句

select中的几个属性说明:

id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。

resultType:将结果集映射为java的对象类型。必须(和 resultMap 二选一)

2.insert

insert 的几个属性说明:

id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致

parameterType:参数的类型,使用动态代理之后和方法的参数类型一致

useGeneratedKeys:开启主键回写

keyColumn:指定数据库的主键

keyProperty:主键对应的pojo属性名

3.update

id属性:当前名称空间下的statement的唯一标识(必须属性);

parameterType:传入的参数类型,可以省略。

标签内部:具体的sql语句

4.delete

delete 的几个属性说明:

id属性:当前名称空间下的statement的唯一标识(必须属性);

parameterType:传入的参数类型,可以省略。

标签内部:具体的sql语句。

#{}和${}

场景:数据库有两个一模一样的表。历史表,当前表

查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。

希望使用1个方法来完成操作。

<select id="queryUserByTableName" resultType="com.spiritmark.mybatis.pojo.User">
select * from #{tableName}
</select> /**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
*
* @param tableName
* @return
*/
public List<User> queryUserByTableName(String tableName);

测试输出:



有问题,报语法错误:相当于执行了这样一条sql:

select * from “tb_user”;

显然表名多了引号。

改正:

<select id="queryUserByTableName" resultType="com.spiritmark.mybatis.pojo.User">
select * from ${tableName}
</select>

注意:

#{} 只是替换?,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。

是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;{} 是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;可以是sql中的任一部分传入到Statement中,不能防止sql注入。

使用去取出参数值信息,需要使用{} 去取出参数值信息,需要使用去取出参数值信息,需要使用{value}

#{} 只是表示占位,与参数的名字无关,如果只有一个参数,会自动对应。

推荐:

/**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
*
* @param tableName
* @return
*/
public List<User> queryUserByTableName(@Param("tableName") String tableName); <select id="queryUserByTableName" resultType="com.spiritmark.mybatis.pojo.User">
select * from ${tableName}
</select>

#{}多个参数时:

/**
* 登录(直接使用注解指定传入参数名称)
*
* @param userName
* @param password
* @return
*/
public User login( String userName, String password); <select id="login" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>

报错:

org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]

解决方案一:

<select id="login" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where user_name = #{0} and password = #{1}
</select>

解决方案二:

<select id="login" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where user_name = #{param1} and password = #{param2}
</select>

最终解决方案:

/**
* 登录(直接使用注解指定传入参数名称)
*
* @param userName
* @param password
* @return
*/
public User login(@Param("userName") String userName, @Param("password") String password); <select id="login" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>

通常在方法的参数列表上加上一个注释@Param(“xxxx”) 显式指定参数的名字,然后通过KaTeX parse error: Expected 'EOF', got '#' at position 10: {“xxxx”}或#̲{“xxxx”}
sql语句动…{};

sql语句中某个参数进行占位的时候#{}

面试题(#、$区别)

/**
* #号
* @param username1
* @return
*/
User queryUserListByName1(@Param("username1") String username1); /**
* $号
* @param username2
* @return
*/
User queryUserListByName2(@Param("username2") String username2); <select id="queryUserListByName1" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user WHERE user_name=#{username1}
</select> <select id="queryUserListByName2" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user WHERE user_name='${username2}'//手动加了引号
</select>

resultMap





使用:

sql片段

<sql id=””></sql>
<include refId=”” />

例如在UserMapper.xml中定义如下片段:

<sql id="commonSql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>

则可以在UserMapper.xml中使用它:

<select id="queryUserById" resultMap="userResultMap">
select <include refid="commonSql"></include> from tb_user where id = #{id}
</select> <select id="queryUsersLikeUserName" resultType="User">
select <include refid="commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>

Sql片段也可以定义在单独的.xml文件中如:

定义CommonSQL.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="CommonSQL">
<sql id="commonSql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>
</mapper> ```xml
<select id="queryUserById" resultMap="userResultMap">
select <include refid="CommonSQL.commonSql"></include> from tb_user where id = #{id}
</select> <select id="queryUsersLikeUserName" resultType="User">
select <include refid="CommonSQL.commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>

当然要完成这个功能还需要在全局配置文件mybatis-config.xml中引入该外部配置文件:

<mappers>
<mapper resource="CommonSQL.xml"/>
<!-- 开启mapper接口的包扫描,基于class的配置方式 -->
<package name="com.spiritmark.mybatis.mapper"/>
</mappers>

动态sql

场景:查询男性用户,如果输入了姓名,按姓名模糊查询

1.if

场景:查询男性用户,如果输入了姓名,则按姓名查询

定义接口:

**
* 查询男性用户,如果输入了姓名,则按姓名查询
* @param name
* @return
*/
List<User> queryUserList(@Param("name") String name);

编写mapper

<select id="queryUserList" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
</select>

测试

@Test
public void testqueryUserList() {
List<User> users = this.userMapper.queryUserList(null);
for (User user : users) {
System.out.println(user);
}
}

2.choose when otherwise

场景:查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“彬啊子”的用户。

定义接口 :

/**
* 查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。
* @param name
* @param age
* @return
*/
List<User> queryUserListByNameOrAge(@Param("name") String name,@Param("age") Integer age);

编写mapper

<select id="queryUserListByNameOrAge" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<!--
1.一旦有条件成立的when,后续的when则不会执行
2.当所有的when都不执行时,才会执行otherwise
-->
<choose>
<when test="name!=null and name.trim()!=''">
and name like '%${name}%'
</when>
<when test="age!=null">
and age = #{age}
</when>
<otherwise>
and name='彬啊子'
</otherwise>
</choose>
</select>

测试

@Test
public void queryUserListByNameOrAge() throws Exception {
List<User> users = this.userMapper.queryUserListByNameOrAge(null, 16);
for (User user : users) {
System.out.println(user);
}
}

3.where 和set

场景一:查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立。

定义接口 :

/**
* 查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立
* @param name
* @param age
* @return
*/
List<User> queryUserListByNameAndAge(@Param("name") String name,@Param("age") Integer age);

编写mapper

<select id="queryUserListByNameAndAge" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user
<!--如果多出一个and,会自动去除,如果缺少and或者多出多个and则会报错-->
<where>
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>

测试

@Test
public void queryUserListByNameAndAge() throws Exception {
List<User> users = this.userMapper.queryUserListByNameAndAge("彬啊子", 20);
for (User user : users) {
System.out.println(user);
}
}

场景二:修改用户信息,如果参数user中的某个属性为null,则不修改。

接口:

/**
* 根据id更新用户信息
*
* @param user
*/
public void updateUser(User user);

配置:

<update id="updateUser" parameterType="com.spiritmark.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update>

测试:

@Test
public void testUpdateUser() {
User user = new User();
user.setBirthday(new Date());
user.setName("静静");
user.setPassword("123456");
user.setSex(0);
user.setUserName("Jinjin");
user.setId("1");
this.userMapper.updateUser(user);
}

4.foreach

场景:按照多个id查询用户信息

定义接口:

/**
* 按多个Id查询
* @param ids
* @return
*/
List<User> queryUserListByIds(@Param("ids") String[] ids);

编写mapper

<<select id="queryUserListByIds" resultType="com.spiritmark.mybatis.pojo.User">
select * from tb_user where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>

测试

@Test
public void queryUserListByIds() throws Exception {
List<User> users = this.userMapper.queryUserListByIds(new String[]{"1","2"});
for (User user : users) {
System.out.println(user);
}
}

If: testognl表达式或者简单java代码

Choose when otherwise—>相当于if else if else

When test参考if

Where set 都有一定的纠错功能

Trim:prefix suffix prefixOverrides suffixOverrides

Foreach:collection item saparator

各位大佬,小弟才16 岁 ,写的不好地方,大家可以提出建议 一起交流 我会继续加油的

持久层之 MyBatis: 第二篇 :动态SQL And多表查询的更多相关文章

  1. mybatis第二天——动态SQL与关联查询

    大纲摘要: 1.输入映射和输出映射 a) 输入参数映射 b) 返回值映射 2.动态sql a) If b) Where c) Foreach d) Sql片段 3.关联查询 a) 一对一关联 b) 一 ...

  2. 持久层之 MyBatis: 第一篇:快速入门

    MyBatis入门到精通 JDBC回顾 1.1.认识MyBatis 1.1.使用IDEA创建maven工程 1.2.引入mysql依赖包 1.3.准备数据 1.4 使用JDBC手写MyBatis框架 ...

  3. mybatis 详解------动态SQL

    mybatis 详解------动态SQL   目录 1.动态SQL:if 语句 2.动态SQL:if+where 语句 3.动态SQL:if+set 语句 4.动态SQL:choose(when,o ...

  4. 【mybatis深度历险系列】mybatis中的动态sql

    最近一直做项目,博文很长时间没有更新了,今天抽空,学习了一下mybatis,并且总结一下.在前面的博文中,小编主要简单的介绍了mybatis中的输入和输出映射,并且通过demo简单的介绍了输入映射和输 ...

  5. 从零搭建springboot服务02-内嵌持久层框架Mybatis

    愿历尽千帆,归来仍是少年 内嵌持久层框架Mybatis 1.所需依赖 <!-- Mysql驱动包 --> <dependency> <groupId>mysql&l ...

  6. Mybatis映射文件动态SQL语句-01

    因为在很多业务逻辑复杂的项目中,往往不是简单的sql语句就能查询出来自己想要的数据,所有mybatis引入了动态sql语句, UserMapper.xml <?xml version=" ...

  7. Mybatis入门之动态sql

    Mybatis入门之动态sql 通过mybatis提供的各种标签方法实现动态拼接sql. 1.if.where.sql.include标签(条件.sql片段) <sql id="sel ...

  8. mybatis中的动态SQL

    在实际开发中,数据库的查询很难一蹴而就,我们往往要根据各种不同的场景拼接出不同的SQL语句,这无疑是一项复杂的工作,我们在使用mybatis时,mybatis给我们提供了动态SQL,可以让我们根据具体 ...

  9. 基于ACCESS和ASP的SQL多个表查询与计算统计代码(一)

    近期在写几个关于"Project - Subitem - Task"的管理系统,说是系统还是有点夸大了,基本就是一个多表查询调用和insert.update的数据库操作.仅仅是出现 ...

随机推荐

  1. leetcode117. 填充每个节点的下一个右侧节点指针 II

    给定一个二叉树struct Node {  int val;  Node *left;  Node *right;  Node *next;}填充它的每个 next 指针,让这个指针指向其下一个右侧节 ...

  2. 使用ES替代whoosh全文检索

    目录 1.docker安装ES 1.拉取docker镜像 2.使用docker安装ES 3.在页面中测试 2.使用ES替代whoosh全文检索 2.1 在Django中修改搜索引擎为ES 2.2 命令 ...

  3. 【Azure微服务 Service Fabric 】Service Fabric中应用开启外部访问端口及微服务之间通过反向代理端口访问问题

    问题描述 1) 当成功的在Service Fabric集群中部署了应用后,如何来访问呢?如果是一个Web服务,它的URL又是什么呢? 2) 当Service Fabric集群中,服务之间如需要相互访问 ...

  4. python计算时间差

    前言 之前写代码都是看打印的初始和结束时间然后自己算间隔时间,感觉总是不方便,这不符合python的优雅简洁,于是去寻找简便之道. 方法 time模块计算时间差 import time s_time ...

  5. Linux没有netstat和ifconfig命令问题

    安装了精简版命令行centos7系统,运行netsta查看端口和ifconfig查看IP命令都提示找不到命令. 解决方法: yum search ifconfig 通过yum search 这个命令我 ...

  6. appium快速入门

    appium快速入门 演示官方demo 第一步:启动安卓模拟器 步骤2:启动Appium桌面 step3:准备自动化脚本与待测APK step4:运行测试代码 分析演示 分析Appium的加载流程 使 ...

  7. linux常见目录

  8. Java基础教程——Java简介

    Java是SUN公司1995年推出的一门高级编程语言,是一种面向Internet的编程语言,随着Java技术在Web方面的不断成熟,已经成为Web应用程序的首选开发语言. (2009年SUN公司被Or ...

  9. Windows生产力工具推荐

    相信大部分同学还是Windows用户,作为一个长期Windows/MacOS双系统长期用户,Windows在用的好,工作效率也很高,下面就推荐几款Windows下面的生产力工具. utools 用过M ...

  10. moviepy音视频剪辑:lum_contrast什么时候使用以及图像处理什么时候需要调整亮度与对比度

    ☞ ░ 前往老猿Python博文目录 ░ 一.亮度.对比度的概念 图像的亮度(luminosity )也即对明度的度量(参考<音视频处理基础知识扫盲:数字视频YUV像素表示法以及视频帧和编解码概 ...