1. 首页
  2. IT资讯

【java框架】MyBatis(1)–MyBatis入门

1.   MyBatis认识

MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis可以使用简单的XML配置或注解来配置和映射原生信息,将接口和Java的POJO(Plain Ordinary Java Object,普通Java对象)映射成数据库中的数据。

2.   使用MyBatis完成基础的CRUD

2.1.导入对应MyBatis及数据库连接jar包

asm-3.3.1.jar

cglib-2.2.2.jar

commons-logging-1.1.1.jar

javassist-3.17.1-GA.jar

log4j-1.2.17.jar

mybatis-3.2.1.jar

mysql-connector-java-5.1.26-bin.jar

slf4j-api-1.7.2.jar

slf4j-log4j12-1.7.2.jar

【java框架】MyBatis(1)--MyBatis入门

2.2.建立数据库的表

【java框架】MyBatis(1)--MyBatis入门

2.3. 对应数据库表创建Domain类

复制代码
public class Product {
private Long id;
private Long dir_id;    // 商品分类编号
private String productName;// 商品名称
private String supplier; // 供应商
private String brand; // 品牌
private Double salePrice;  // 零售价
private Double cutoff; // 折扣比例
private Double costPrice; // 进价
。。。。// Getter与Setter略过
}
复制代码

2.4. 配置MyBatis核心配置文件

MyBatis的核心配置文件放在项目的资源文件resources目录下,文件命名为mybatis-config.xml,具体的配置如下:

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>
    <!-- 引入jdbc.properties配置文件 -->
    <properties resource="jdbc.properties"></properties>
    <!--别名的配置,类的全限定名可以使用别名
        alias:别名名称
        type:别名所对应的类的全限定名,别名的使用与大小写无关-->
    <typeAliases>
        <typeAlias alias="Product" type="cn.yif.mybatis.domain.Product"></typeAlias>
        <typeAlias alias="User" type="cn.yif.mybatis.domain.User"></typeAlias>
    </typeAliases>
    <!--MyBatis环境配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 数据库连接配置 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
</environments>
<!—- Mapper.xml文件配置 -->
    <mappers>
        <mapper resource="cn/yif/mybatis/mapper/ProductMapper.xml"/>
        <mapper resource="cn/yif/mybatis/mapper/UserMapper.xml"/>
    </mappers>
</configuration>
复制代码

2.5. 关联jdbc.properties文件配置

driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis01_0317
username = root
password = admin

2.6. 对象关系映射文件XXXMapper.xml

映射文件XXXMapper.xml是对应Domain属性与DaoImpl中方法并与数据库字段产生关系的唯一映射配置,在这里面我们可以存放基础的CRUD对应的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">
<!-- namespace:用来找唯一的mapper文件,一般是domain的全路径名+Mapper来命名 -->
<mapper namespace="cn.yif.mybatis.domain.Product">
    <!--id表示唯一标识,parameterType标识参数的类型,resultType表示返回值的类型-->
    <select id="queryById" parameterType="Long" resultType="cn.yif.mybatis.domain.Product">
        select * from Product where id = #{id}
    </select>
    <select id="queryAll" resultType="cn.yif.mybatis.domain.Product">
        select * from Product
    </select>
    <!--添加时拿到返回的主键:
        parameterType:需要传入的对象
        useGeneratedKeys:是否需要主键
        keyColumn:数据库中对应主键的列
        keyProperty:对象中主键对应的id-->
   <insert id="insert" parameterType="cn.yif.mybatis.domain.Product" useGeneratedKeys="true"
            keyColumn="id" keyProperty="id">
       insert into Product(productName, dir_id, salePrice, supplier, brand, cutoff, costPrice)
        values (#{productName}, #{dir_id}, #{salePrice}, #{supplier}, #{brand}, #{cutoff}, #{costPrice})
    </insert>
    <update id="update" parameterType="cn.yif.mybatis.domain.Product">
        update product set productName = #{productName}, dir_id = #{dir_id}, salePrice = #{salePrice},
        supplier = #{supplier}, brand = #{brand}, cutoff = #{cutoff}, costPrice = #{costPrice}
        where id = #{id}
    </update>
    <delete id="delete" parameterType="Long">
        delete from product where id = #{id}
    </delete>
</mapper>
复制代码

2.7. 获取SqlSession对象并抽取工具Util类

首先我们需要读取mybatis-config.xml核心资源文件,通过SqlSessionFactoryBuilder构建者来创建一个SqlSessionFactory工厂,通过SqlSessionFactory来创建SqlSession对象。通过SqlSession就可以执行我们的数据库增删查改的xml中的配置语句,最终使用之后还需要及时关闭SqlSession语句对象。基于这种增删改查操作都是同样的我们可以抽取一个工具类MyBatisUtil,具体实现如下:

复制代码
public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory = null;
    private static Reader reader = null;

    // 因为sqlSessionFactory是线程安全的,每次初始化只需要创建一个SqlSessionFactory实例
    static {
        try {
            reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // SqlSession是线程不安全的类,每个线程内单独去获取当前线程的SqlSession
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }

    // 关闭SqlSession
    public static void closeSqlSession(SqlSession sqlSession){
        if(sqlSession != null){
            sqlSession.close();
        }
    }
}
复制代码

2.8. 对应实现增删查改的DaoImpl代码

ProductDaoImpl.java

复制代码
public class ProductDaoImpl implements IProductDao {
    public static final String NAME_SPACE = "cn.yif.mybatis.domain.Product.";

    @Override
    public void insert(Product product) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        sqlSession.insert(NAME_SPACE + "insert", product);
        MyBatisUtil.closeSqlSession(sqlSession);
    }

    @Override
    public void update(Product product) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        sqlSession.update(NAME_SPACE + "update", product);
        MyBatisUtil.closeSqlSession(sqlSession);
    }

    @Override
    public void delete(Long id) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        sqlSession.delete(NAME_SPACE + "delete", id);
        MyBatisUtil.closeSqlSession(sqlSession);
    }

    @Override
    public Product queryById(Long id) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        Product product = sqlSession.selectOne(NAME_SPACE + "queryById", id);
        MyBatisUtil.closeSqlSession(sqlSession);
        return product;
    }

    @Override
    public List<Product> queryAll() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        List<Product> list = sqlSession.selectList(NAME_SPACE + "queryAll");
        MyBatisUtil.closeSqlSession(sqlSession);
        return list;
    }
}
复制代码

2.9. 对应Junit4测试类实现

复制代码
public class ProductDaoImplTest {
    @org.junit.Test
    public void insert() {
        IProductDao productDao = new ProductDaoImpl();
        Product product = new Product();
        product.setProductName("测试产品112");
        product.setBrand("TestBrand");
        product.setCostPrice(237.5);
        product.setCutoff(0.85);
        product.setDir_id(4L);
        product.setSalePrice(520.0);
        product.setSupplier("甲骨文");
        System.out.println("添加之前,查看id:" + product.getId());
        productDao.insert(product);
        System.out.println("添加之后,查看id:" + product.getId());
    }

    @org.junit.Test
    public void update() {
        IProductDao productDao = new ProductDaoImpl();
        Product product = new Product();
        product.setId(22L);
        product.setProductName("测试产品114");
        product.setBrand("TestBrand114");
        product.setCostPrice(237.1);
        product.setCutoff(0.45);
        product.setDir_id(4L);
        product.setSalePrice(520.1);
        product.setSupplier("甲骨文114");
        productDao.update(product);
   }

    @org.junit.Test
    public void delete() {
        IProductDao productDao = new ProductDaoImpl();
        productDao.delete(24L);
    }

    @org.junit.Test
    public void queryById() {
        IProductDao productDao = new ProductDaoImpl();
        Product product = productDao.queryById(31L);
        System.out.println(product);
    }

    @org.junit.Test
    public void queryAll() {
        IProductDao productDao = new ProductDaoImpl();
        List<Product> list = productDao.queryAll();
        for (Product product: list) {
            System.out.println(product);
        }
    }
}
复制代码

3.   MyBatis配置使用细节

3.1. 添加insert时拿到返回的主键

对应在XXXMapper.xml中需要做如下配置:

复制代码
<!--添加时拿到返回的主键:
        parameterType:需要传入的对象
        useGeneratedKeys:是否需要主键
        keyColumn:数据库中对应主键的列
        keyProperty:对象中主键对应的id-->
<insert id="insert" parameterType="cn.yif.mybatis.domain.Product" useGeneratedKeys="true"
            keyColumn="id" keyProperty="id">
        insert into Product(productName, dir_id, salePrice, supplier, brand, cutoff, costPrice)
        values (#{productName}, #{dir_id}, #{salePrice}, #{supplier}, #{brand}, #{cutoff}, #{costPrice})
</insert>
复制代码

3.2. Log4j日志配置

Log4j是MyBatis提供的日志打印依赖jar包,可以帮助我们进行分析和定位问题。在Log4j日志配置文件中我们可以规定日志打印显示的级别:

日志等级:从低到高(大小写没有关系,但是在配置的时候都建议使用大写)

等级从低到高(大小写没有关系,但是在配置的时候建议都写大写)
TRACE:详细  (建议开发的时候用)
DEDUG:调试,类似于System.out.print
INFO:信息,类似于JPA打印sql等级
WARN:警告,程序可以正常运行,出现提示
ERROR:错误,出现异常  (建议正式环境)

具体配置文件应命名为log4j.properties,配置如下:

log4j.properties:

复制代码
#log4j.properties(日志文件):
#全局配置:先配置一个日志的根,这个级别是ERROR
log4j.rootLogger=ERROR, stdout
#局部配置 :把左边包名改成你自己的包名:表示我们自己这个路径下的代码级别
#级别说明:TRACE(详细)、Debug(调试)、Info(信息)、Warn(警告)、Error(错误)
log4j.logger.cn.yif.mybatis=TRACE
#在控制台输出和输出的格式
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
复制代码

3.3.MyBatis中设置类的别名typeAliases

①    MyBatis的内置别名

MyBatis中包含内置别名,即无需配置,MyBatis框架自动涵盖的别名,可以直接使用。

具体包含如下别名类型:

【java框架】MyBatis(1)--MyBatis入门

②    自定义别名

具体配置及其使用:

别名需要在mybatis-config.xml中使用,位置在<properties>标签之后:

复制代码
<!--别名的配置,类的全限定名可以使用别名
      alias:别名名称
      type:别名所对应的类的全限定名,别名的使用与大小写无关-->
<typeAliases>
    <typeAlias alias="Product" type="cn.yif.mybatis.domain.Product"></typeAlias>
     <typeAlias alias="User" type="cn.yif.mybatis.domain.User"></typeAlias>
</typeAliases>
复制代码

使用别名:

在XXXMapper.xml文件中对应的类全限定名即可使用别名,且与大小写无关。

【java框架】MyBatis(1)--MyBatis入门

3.4.列名与属性名不一致ResultMap

数据库中的列名与Domain类中的属性名不一致时,如果直接使用ResultType对应,执行Sql查询语句等会出现相应值为null的情况,这时需要配置ResultMap进行关系映射对应,告诉Sql这个数据库中的列是对应Domain类中的哪个属性,具体配置如下:

复制代码
<?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">
<!-- namespace:用来找唯一的mapper文件,一般是domain的全路径名+Mapper来命名 -->
<mapper namespace="cn.yif.mybatis.domain.User">
  <!--当Domain中的属性与数据库中的字段列不一致时,需要使用resultMap
        id表示唯一标识,对应select中的resultMap
        type表示返回值类型
        column:对应数据库中的列名
        property:对应Domain中类的属性字段-->
  <resultMap id="userMap" type="User">
      <result column="username" property="name"></result>
      <result column="password" property="pwd"></result>
  </resultMap>
  <!--id表示唯一标识,parameterType标识参数的类型,resultType表示返回值的类型-->
  <select id="getUserById" parameterType="Integer" resultMap="userMap">
        select * from user where id = #{id}
  </select>
</mapper>
复制代码

3.5.参数传递为Map类型

在Mapper接口中可以传入Map类型来查询返回特定的查询对象,使用Map的key与value来对应查询中的#{value值}。具体的使用示例如下:

接口层IXxxDao:

public interface IUserDao {
    //声明一个使用HashMap参数类型的接口
    User getUserByMap(HashMap userMap);
}

实现层XxxDaoImpl:

复制代码
public class UserDaoImpl implements IUserDao {
    @Override
    public User getUserByMap(HashMap userMap) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        User user = sqlSession.selectOne("cn.yif.mybatis.domain.User.getUserByMap", userMap);
        return user;
    }
}
复制代码

对应XxxMapper.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">
<!-- namespace:用来找唯一的mapper文件,一般是domain的全路径名+Mapper来命名 -->
<mapper namespace="cn.yif.mybatis.domain.User">
    <!--参数类型可以是普通类型,也可以是Map类型,获取的时候,根据Map传递的key来取值:
         HashMap<String, String> map = new HashMap();
         map.put("key_name", "蒋工");
         map.put("key_pwd", "1332");
     -->
    <select id="getUserByMap" parameterType="HashMap" resultMap="userMap">
        select * from user where username = #{key_name} and password = #{key_pwd}
    </select>
</mapper>
复制代码

测试类即可通过key/value映射关系查询到对应的User数据如下:

【java框架】MyBatis(1)--MyBatis入门

3.6. #与$传递Sql语句的差别

①    使用#—-简单来说:

#在使用时MyBatis会把这个Sql语句中#{参数名}这一整个部分使用一个?(占位符)替换,在整个Sql语句中作为一个Sql参数使用,在大多数需要传递数据库与Domain属性值的情况下推荐使用#;

具体示例:

定义Sql语句:select * from t_user where name = #{name};

最终Sql语句:select * from t_user where name = ?

②    使用$—-简单来说:

$在使用时MyBatis会把这个${参数名/参数语句}整个表达式值或者表达式部分替换到Sql中,作为Sql语句的组成部分,该方式主要用于拼接Sql语句。

$使用主要是使用在order by排序或者limit分页查询的场景,不能使用在用户名与密码查询的登录场景,会产生Sql注入问题;

具体示例:

数据库里面我有这样一组数据,并且我使用Sql注入来模拟查询出了一组数据,在Mapper.xml中我来模拟一下Sql注入对应#与$的使用区别:

【java框架】MyBatis(1)--MyBatis入门

使用#:

这里#会把”蒋工” or “1=1″(String)整个Sql注入部分当成是一个参数,不会产生Sql注入问题;

【java框架】MyBatis(1)--MyBatis入门

 

使用$:

完全是拼接Sql,会把传入的参数原封不动的拼接在原本的Sql语句上,””都要拼接,最终导致Sql注入;

【java框架】MyBatis(1)--MyBatis入门

 

$在排序分页场景时的使用:

在数据库中我模拟了一条使用order by与limit分页查询的场景,具体查询Sql如下:

【java框架】MyBatis(1)--MyBatis入门

 

对应XxxMapper.xml中实际的Sql查询,如下:

【java框架】MyBatis(1)--MyBatis入门

 

创建Test测试如下,这里使用$拼接Sql的方式查询出了对应的分页显示数据:

【java框架】MyBatis(1)--MyBatis入门

 

本文来自投稿,不代表程序员编程网立场,如若转载,请注明出处:http://www.cxybcw.com/188849.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code