MyBatis

MyBatis

1.什么是 MyBatis?

MyBatis是对jdbc的轻量级封装,是一个orm对象关系映射框架,(通过xml或注解的方式,把实体类与表之间做映射关系),目的是为了简化开发,达到 以OOP的方式来操作数据库(不写sql)。
dbutils是一个jdbc组件;
mybatis–>mybatis-plus(大大提高开发效率)
mybatis是一个半自动的orm框架,还要写sql.

2.使用mybatis

​ 2.1 安装环境:
​ mybatis的核心jar包;数据库驱动包
​ 2.2 创建xml文件:
​ xml配置文件mybatis-config.xml,一般这个文件放src根目录
​ xml映射文件blog-mapper.xml,这个文件放dao包下;
​ 把映射文件放在配置文件中;

// 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>
   <!--加载外部properties资源文件,通过${}表达式根据key获取值-->         //可没有
    <properties resource="jdbc.properties"></properties> 
    
   <!--数据库环境配置-->
    //就是一个默认的数据库id     一定是下面的数据库的id 
    //如果是多数据库环境 
    //SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(res,"dev"); 
    //dev就是对应的数据库环境 不加就是默认的数据库环境
    <environments default="development">     
        <!--单数据库 环境,一个environment对应的SqlSessionFactory-->
        <environment id="development">
            <!--事务管理机制:JDBC有事务|MANAGED无事务-->
            <transactionManager type="JDBC"/>
            <!--数据库连接池:UNPOOLED(不使用)|POOLED(使用)|JNDI(已过时)-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射文件:统一写sql语句-->
    <mappers>
        <!--把映射文件包含进来-->   //可添加多个
        <mapper resource="com/javasm/sys/mapper/myblog.xml"></mapper>    //把映射文件放在配置文件中;
    </mappers>
</configuration>
    
    
    
//xml映射文件blog-mapper.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="aaa">                                       //可改
    <select id="selectBlog" resultType="com.javasm.sys.enity.TestUser">     //id-方法名字   resultType-返回类型
    //sql语句   testuser-表的名字     bid-表里的列名字    #{bid}-实体类对应的属性
    select * from testuser where bid = #{bid}                
  </select>
</mapper>    

​ 2.3 从xml文件构建SqlsessionFactory对象
​ 该对象的创建,需要依赖数据源;需要映射文件信息
​ 2.4 获取 SqlSession
​ 2.5 执行操作

public class Test01 {
    public static void main(String[] args) {
        //1.加载xml配置文件,创建SqlSessionFactory对象
        InputStream res = Test01.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        //解析mybatis-config中的数据,读取到内存,相当于DataSource
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(res);
        //2.获取sqlSession对象,相当于Connection
        SqlSession s = ssf.openSession();
        //3.执行数据库操作,因为解析xml映射的时候,内部吧namespace.id拼接作为key,
        //这里的aaa就是 xml映射文件里的 <mapper namespace="aaa"> 
        Object o = s.selectOne("aaa.selectBlog",1);
        System.out.println(o);
        //关闭连接
        s.close();       
    }
}

3.核心对象:

   SqlSessionFactoryBuilder:
        用来加载文件,创建Facotyr,用完即销毁。
    SqlSessionFactory:
        该对象全局唯一。
        openSession()
    SqlSession:
        执行数据库操作,openSession创建对象,执行操作,操作完成后关闭对象。
        Object obj = selectOne("映射文件的namespace.标签id",查询参数)
        List<Object> selectList("namesapce.id",查询参数)
        T t = getMapper(Class<T> clz)

4.settings标签与日志

<?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>
    <properties resource="jdbc.properties"></properties>

    <settings>
        <!--运行日志配置-->  //在运行过程中把sql语句打印     //name-表示是在配置运行日志     //value对应运行日志的类型 
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启驼峰命名映射--> //支持驼峰映射,把下划线分割的字段映射成实体类驼峰命名
       //例如数据库字段名是create_name  实体类是createTime  有了这个配置后就不会报错
        <setting name="mapUnderscoreToCamelCase" value="true"></setting>
    </settings>

    <!--只针对于实体类,进行包扫描配置别名,别名即类名,忽略大小写-->    接口不可配置   类名绝对不能重复
    <typeAliases>
        <package name="com.javasm"></package>    //这个是把com.javasm里的类都扫描自动生成别名了
    </typeAliases>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com\javasm\sys\mapper\blog-mapper.xml"></mapper>  
        <mapper resource="com\javasm\sys\mapper2\blog-mapper2.xml"></mapper>
    </mappers>
</configuration>
内置别名

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

5.mapper映射

<?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="aaa">
    
/* 返回对象
    <!--简单类型类型:8个包装类对象,String,Date-->
    <!--public Blog selectBlog(Integer bid)-->
    <!--select:表示查询,id:类似于dao接口的方法名,resultType:返回值类型,parameterType:参数类型-->
    <!parameterType:参数类型  可不写-->
*/
    <select id="selectBlog" parameterType="Integer" resultType="TestUser">
    select * from testuser where bid = #{bid}
    </select>
        
/*  返回List
    <!--public List<Blog> selectBlogs(Blog b)-->
    <!--resultType在mybatis内部表示对数据库表的单行记录封装的类型(实体,Map)-->
    <!--#{实体类的成员变量名}-->
 */ 
    <select id="selectList" resultType="TestUser">
    select * from testuser where bid = #{bid}
    </select>
               
/*  返回Map
    <!--public List<Map<String,Object>> selectBlogs2Map(Blog b)-->
 */ 
    <select id="selectMap" resultType="Map" >
    select * from testuser where bname like "%"#{bname}"%"
    </select>
</mapper>
public class Test01 {
    public static void main(String[] args) {
        InputStream res = Test01.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(res);

        SqlSession s = ssf.openSession();

//只从数据库取一个对象或数据的时候 调用selectOne()方法
        Object o = s.selectOne("aaa.selectBlog",1);
        
//从数据库取List集合时 调用selectList()
        List<Object> o = s.selectList("aaa.selectList",1);

//Map集合时 也是调用selectList()        
        TestUser b = new TestUser();
        b.setBname("a");
        List<Map<String,Object>> o = s.selectList("aaa.selectMap", b);
        System.out.println(o);

        s.close();
    }
}
mapper映射文件中的注意点:
  • namespace:必须唯一,不同文件不能同名的namesapce
  • 查询语句的标签必须是select,id在同一个mapper文件中不能同名,paramterType:表示参数类型;resultType:返回值类型,表示对数据库表单行记录的封装类型。
  • 在sql语句中获取参数的语法:#{简单类型的随便写,对象类型的写成员变量名,}

6.SqlSession的getMapper方法

  • 映射文件的namespace必须是接口的全名称
  • id必须是接口中的方法名
//1.先写接口
public interface BlogMapper {
    //传入整数 返回实体类对象
    public TestUser selectById(Integer bid);
    //传入对象 返回实体类对象
    public TestUser selectTestUser(TestUser b);
    //传入map集合 返回实体类对象
    public TestUser selectTestUserMapParam(Map<String,Object> map);
    
    //内部仍然是把参数封装为map,把@Param注解的值作为key
    public TestUser selectTestUserParam(@Param("bid_param")Integer bid, @Param("bname_param") String bname);
    
    //传入注解值  返回List  
    public List<TestUser> selectTestUsers(@Param("by") String by, @Param("sort") String sort);
    
    //传入整数 返回数据库字段内容
    public String selectTitleById(Integer bid);
}
//2. 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 namespace="com.javasm.sys.mapper.BlogMapper">
    <!--id:必须是接口的方法名,-->
    <select id="selectById" resultType="TestUser">
        <!--#{bid} 这个里面随便写 #{a}  #{ccc}  #{id}  都可以去得到方法传过来的Integer值 不可以空的#{}-->
        select * from testuser where bid = #{bid}
    </select>

    <!--#{传入对象的成员变量名}-->
    <select id="selectTestUser" resultType="TestUser">
        select * from testuser where bid = #{bid}
    </select>

    <!--#{map的key}-->
    <select id="selectTestUserMapParam" resultType="TestUser">
        select * from testuser where bid = #{bid_key}
    </select>

    <!--#{@Param注解的值}-->
    <select id="selectTestUserParam" resultType="TestUser">
        select * from testuser where bid=#{bid_param} and bname=#{bname_param}
    </select>

    <select id="selectTestUsers" resultType="TestUser">
        select * from testuser order by ${by} ${sort}
    </select>

    <select id="selectTitleById" resultType="string">
        select bname from testuser  where bid=#{bid}
    </select>
</mapper>
//3.运行
public class TestGetMapper {
    public static void main(String[] args) {
        InputStream in = TestGetMapper.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in);
        SqlSession s = ssf.openSession();
        BlogMapper mapper = s.getMapper(BlogMapper.class);
          //传入整数 返回实体类对象
        TestUser blog = mapper.selectById(1);

         //传入对象 返回实体类对象
        TestUser t = new TestUser();
        t.setBid(1);
        TestUser blog = mapper.selectTestUser(t);

//        传入map集合 返回实体类对象
        Map<String,Object> map = new HashMap<>();
        map.put("bid_key",2);
        System.out.println(map);
        TestUser blog = mapper.selectTestUserMapParam(map);

//        内部仍然是把参数封装为map,把@Param注解的值作为key
        TestUser blog = mapper.selectTestUserParam(3,"bbb");

//        传入注解值  返回List
        List<TestUser> blog = mapper.selectTestUsers("bid", "desc");

//        传入整数 返回数据库字段内容
        String blog = mapper.selectTitleById(1);

        System.out.println(blog);
        s.close();
    }
}    

7.如何多参数传递

​ 5.1 封装实体
​ 5.2 封装map,适合于多参数不属于同一个实体
​ 5.3 加注解@Param,加到mapper接口的方法形参上

例子参考上述例子
//额外知识

//单例模式
public class SSFUtil {
    private static SqlSessionFactory ssf;//谁单例谁静态

    static{
        //给ssf对象实例化
        InputStream in = null;
        try {
            in = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        ssf = new SqlSessionFactoryBuilder().build(in);
    }

    public static SqlSessionFactory getFactory(){
        return ssf;
    }
}

//运行
public class TestGetMapper {
    public static void main(String[] args) {
        SqlSessionFactory factory = SSFUtil.getFactory();
        SqlSession s = factory.openSession();
        
        BlogMapper mapper = s.getMapper(BlogMapper.class);
          //传入整数 返回实体类对象
        TestUser blog = mapper.selectById(1);

        System.out.println(blog);
        s.close();
    }
}  

8.跟踪源码:

XMLConfigBuilder:xml配置文件解析对象,构建器build模式,用来构建对象。
    Configuration configuration
    parse(),返回Configuration配置对象。
XMLMapperBuilder:xml映射文件的解析对象,最重要的是得到mappedStatements集合
    parse(),解析mapper.xml,重点在于解析SELECT|INSERT|UPDATE|DELETE四类标签

Configuration:配置对象,把xml配置文件数据读取到该对象
    Environment:数据库环境,数据库连接信息
    TypeAliasRegistry:类型别名注册,别名信息
    Map<String, MappedStatement> mappedStatements:sql映射结果集,key:namespace.id,value:MappedStatement(操作类型,sqlResource)
    variables:properties资源配置信息。

DefaultSqlSessionFactory implements SqlSessionFactory
    Configuration configuration

MappedStatement:相当于一个select|update|delete|insert标签的解析结果

DefaultSqlSession implements SqlSession
    Configuration configuration;

Executor:执行器对象。
    用来执行具体的数据库操作

总结:
    SqlsessionFactoryBuilder.build()
        XMLConfigBuilder与XMLMapperBuilder是为了解析xml配置与xml映射文件,得到Configuration对象
        创建出SqlsessionFactory对象,持有Configuration对象对象:new DefaultSqlSessionFactory(Configuration config)
    opensession()
        得到Executor执行器对象。打开Connection连接
    selectOne或selectList的时候:
        根据传递进去的String(namespace.id)去Configuration得到一个具体的MappedStatement对象,执行query方法进行数据库查询,结果集封装。
    getMapper方法:
        该方法传入接口的类对象,返回代理对象。

9.代理模式:proxy

​ 作用:当一个已经存在的对象中的某个方法不满足需求,此时需要代理模式。
​ 静态代理:
​ 创建代理类,必须与被代理对象同类型;代理对象必须持有被代理对象。

​ 动态代理:
​ 代理类不再手工创建,在程序运行期间动态的在jvm内创建的代理类,并编译。

//静态代理


//连接代理
public class ConnectionProxy implements Connection {
    private Connection source;
    private MyPool pool;

    public ConnectionProxy(Connection source,MyPool pool) {
        this.source = source;
        this.pool= pool;
    }

    @Override
    public Statement createStatement() throws SQLException {
        return this.source.createStatement();
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        this.source.setAutoCommit(autoCommit);
    }
    
    
    //这个方法不需要调用原生的jdbc的close方法
    //别的省略没写.........太长了
    @Override
    public void close() throws SQLException {
        pool.addConnection(this);//此处的this表示ConnectionProxy对象
        System.out.println("放回连接池");
    }
}


//连接池
public class MyPool {
    //链表,删除插入快,查询慢
    private LinkedList<Connection> list = new LinkedList<>();

    public void init() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        for(int i=0;i<10;i++){
            //1.原生的jdbc的连接对象,被代理对象
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/three_project", "root", "root");
            //2.为原生connection加一层代理对象
            Connection proxy = new ConnectionProxy(connection,this);//此处的this时MyPool对象
            addConnection(proxy);
        }
    }

    public Connection getConnection(){
        System.out.println("获取前,当前大小:"+list.size());
        Connection pop = list.pop();
        System.out.println("获取后,当前大小:"+list.size());
        return pop;
    }

    public void addConnection(Connection conn){
        list.push(conn);
    }
}


//运行
public class TestConnection {
    public static void main(String[] args) throws Exception {
        MyPool pool = new MyPool();
        pool.init();

        Connection connection = pool.getConnection();
        //TODO 数据库操作
        PreparedStatement preparedStatement = connection.prepareStatement("select * from blogs");
        System.out.println(preparedStatement);
        connection.close();//希望close方法不要关闭连接,而是返回list

        Connection connection2 = pool.getConnection();
        //TODO 数据库操作
        connection2.close();
    }
}

10.#{}与${}:

​ 写在映射文件中,sql语句获取参数.
​ #{} sql语句使用?占位符,PreparedStatement.
不 使 用 占 位 符 , S t a t e m e n t , 平 常 不 用 , 但 是 如 果 参 数 是 表 名 , 字 段 名 等 s q l 语 句 的 语 法 部 分 。 必 须 使 用 {} 不使用占位符,Statement,平常不用,但是如果参数是表名,字段名等sql语句的语法部分。必须使用 使Statementsql使{}
​ select ${} from blogs where bid=#{} order by ${} ${}

11.公共sql语句块

   //id名字随意命名  不过要对应下边的<include refid="allFields"></include>
	<sql id="allFields">
        uid,uname,uphone,upwd,uemail,create_time,update_time,rid
    </sql>
    
        
    <select id="selectUserById" resultType="Sysuser">
        select
         <include refid="allFields"></include>
         from sysuser where uid = #{uid}
    </select>        

12.动态sql

动态sql:在xml映射文件中进行sql拼接
    if:条件判断,传入boolean表达式,多条件使用and或or
    where:生成where关键字,并忽略紧跟其后的and或or,如果where中间的是空的,则不生成where了
    set:生成set关键字,并忽略最后一个逗号.
    foreach:遍历数组和集合参数,批量删除,批量添加
    choose-when-otherwise:单条件查询   只会根据到第一个匹配到的进行查询  不常用
//where:生成where关键字,并忽略紧跟其后的and或or,如果where中间的是空的,则不生成where了
public List<Sysuser> selectUsers(Sysuser suser);//多条件查询

//映射语句
    <select id="selectUsers" resultType="Sysuser" parameterType="Sysuser">
        select * from sysuser
        <where>
          <if test="uid!=null">
              and uid=#{uid}
          </if>
          <if test="uname!=null and uname!=''">
              and uname=#{uname}
          </if>
          <if test="uphone!=null and uphone!=''">
              and uphone=#{uphone}
          </if>
            <if test="upwd!=null and upwd!=''">
                and upwd=#{upwd}
            </if>
            <if test="uemail!=null and uemail!=''">
                and uemail=#{uemail}
            </if>
        </where>
        order by update_time desc
    </select>
//   set:生成set关键字,并忽略最后一个逗号. 
 public int updateUserByIdSelective(Sysuser suser);//选择非空字段更新

//映射语句
	<update id="updateUserByIdSelective" parameterType="sysuser">
      update sysuser
       <set>
           <if test="uname!=null">
               uname=#{uname},
           </if>
           <if test="uphone!=null and uphone!=''">
                uphone=#{uphone},
           </if>
           <if test="upwd!=null and upwd!=''">
                upwd=#{upwd},
           </if>
           <if test="uemail!=null and uemail!=''">
                uemail=#{uemail},
           </if>
       </set>
       where uid=#{uid}
    </update>
//foreach:遍历数组和集合参数,批量删除,批量添加

//批量添加
 public int addUsers(Sysuser[] suser);//批量添加用户

//映射语句
    <insert id="addUsers">
        insert into sysuser(uname,upwd,uphone,uemail,rid) values
        <foreach collection="array" separator="," item="suser">
            (#{suser.uname},#{suser.upwd},#{suser.uphone},#{suser.uemail},#{suser.rid})
        </foreach>
    </insert>
        
//批量删除 
 public int deleteUsersByIds(@Param("uids") Integer[] uids);
//批量删除,mybatis底层把参数封装为map,默认key为:array,map.put("array",Integer[])  

//映射语句
    <!--collection:形参为数组写array|形参为list写list  (1,2,3)-->
    <delete id="deleteUsersByIds">
        delete  from sysuser where uid in
        <foreach collection="uids" open="(" close=")" separator="," item="userid">
          #{userid}
        </foreach>
    </delete>        
//choose-when-otherwise:单条件查询    只会根据到第一个匹配到的进行查询
public List<Sysuser> selectUsersOneCondition(Sysuser suser);//单条件查询

//映射语句
    <select id="selectUsersOneCondition" resultType="Sysuser" parameterType="Sysuser">
        select * from sysuser
        <where>
            <choose>
                <when test="uid!=null">
                    uid=#{uid}
                </when>
                <when test="uname!=null and uname!=''">
                    uname=#{uname}
                </when>
                <when test="uphone!=null and uphone!=''">
                    uphone=#{uphone}
                </when>
                <otherwise></otherwise>
            </choose>
        </where>
        order by update_time desc
    </select>

13 insert|update|deleted

​ 这三个标签都没有resultType属性,操作结果需要靠接口的返回值来判断。返回值int受影响行数.

insert
public interface BlogMapper {
    public int addUser(Trole tro);
    public int addUsers(Trole[] tro);//批量添加用户
    public int addUserAndGetUserId(Trole tro);
}
//映射语句
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
    //如果role_Name或createTime实体类没有内容那么数据库就会赋值null 别的没有的直接默认值或者null
    <insert id="addUser" parameterType="Trole">
    insert into trole ( role_Name,createTime ) values ( #{roleName},#{createTime} )
    </insert>

    //mybatis底层把参数封装为map,默认key为:array,map.put("array",Trole[])
    //如果加入注解 key为:注解的值, 假如多参数传入了gid 里面也可以直接取#{gid}传入的值 
    //foreach就相当于一个循环
    //    <foreach collection="array" separator="," item="tro">
    //       (#{gid},#{tro.createTime})
    //    </foreach>
    <insert id="addUsers">
    insert into trole ( role_Name,createTime ) values
        <foreach collection="array" separator="," item="tro">
            (#{tro.roleName},#{tro.createTime})
        </foreach>
    </insert>

    // 如果数据库是主键自增的,在insert标签中useGeneratedKeys="true" keyProperty="形参对象的属性名" 获取自增主键id
    //然后通过实体类.getrid获取
    <insert id="addUserAndGetUserId" parameterType="Trole" useGeneratedKeys="true" keyProperty="rid" >
    insert into trole ( role_Name,createTime ) values ( #{roleName},#{createTime} )
    </insert>
</mapper>
//Test
public class TsetInser {
    private static SqlSessionFactory factory = null;
    private SqlSession s = null;
    
    //最开始的时候走这里一次  ----------  @BeforeClass
    @BeforeClass
    public static void init(){
        factory = SessionFactoryUtil.getFactory();
    }

    //每次进方法的时候走这里一次  ----------  @Before
    @Before
    public void oppension(){
        s = factory.openSession();
    }

    
    @Test
    public void test1_addUser(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        Trole user = new Trole();
        user.setRoleName("员工si");
        int i = mapper.addUser(user);
        s.commit();
        System.out.println(i);
    }

    @Test
    public void test1_addUsers(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        Trole a = new Trole();
        Trole b = new Trole();
        Trole c = new Trole();
        a.setRoleName("a");
        b.setRoleName("b");
        c.setRoleName("c");
        Trole[] user = new Trole[]{a,b,c};
        int i = mapper.addUsers(user);
        s.commit();
        System.out.println(i);
    }

    @Test
    public void test1_addUserAndGetUserId(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        Trole user = new Trole();
        user.setRoleName("员工s");
        int i = mapper.addUserAndGetUserId(user);
        s.commit();
        System.out.println(i);
        System.out.println(user.getRid());
    }

    //每个方法退出后走这里一次  ----------  @After
    @After
    public void closeSession(){
        s.close();
    }
    
   //最后的最后走这里一次  ----------   @AfterClass
    @AfterClass
    public static void destory(){
        factory=null;
        System.out.println("destory");
    }
}

update
public interface BlogMapper {
    public int updateUserById(Trole tro);//全字段更新
    public int updateUserByIdSelective(Trole tro);//选择非空字段更新
}
//update映射语句
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
    
    //只更新sql包括的内容 没有复制的会把数据库里已有的内容改成null
    <update id="updateUserById" parameterType="Trole">
    update trole set role_Name=#{roleName},createTime=#{createTime} where r_id=#{rid}
    </update>

    //通过set if标签实现  选择非空字段更新   
    <update id="updateUserByIdSelective" parameterType="Trole">
        update trole
        <set>
            <if test="roleName!=null">
                role_Name=#{roleName},
            </if>
            <if test="createTime!=null and createTime!=''">
                createTime=#{createTime},
            </if>
        </set>
        where r_id=#{rid}
    </update>
</mapper>        
public class TestUpdate {
    private static SqlSessionFactory factory = null;
    private SqlSession s = null;

    @BeforeClass
    public static void init(){
        factory = SessionFactoryUtil.getFactory();
    }

    @Before
    public void oppension(){
        s = factory.openSession();
    }


    @Test
    public void test1_upUser(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        Trole user = new Trole();
        user.setRoleName("ccccc");
        user.setRid(16);
        int i = mapper.updateUserById(user);
        s.commit();
        System.out.println(i);
    }

    @Test
    public void test1_upUser2(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        Trole user = new Trole();
        user.setRoleName("ssssss");
        user.setRid(14);
        int i = mapper.updateUserByIdSelective(user);
        s.commit();
        System.out.println(i);
    }

    @After
    public void closeSession(){
        s.close();
    }

    @AfterClass
    public static void destory(){
        factory=null;
        System.out.println("destory");
    }
}
deleted
public interface BlogMapper {
    public int deleteUserById(Integer uid);
    //批量删除,mybatis底层把参数封装为map,默认key为:array,map.put("array",Integer[])
    public int deleteUsersByIds(@Param("rids") Integer[] rids);
    //批量删除mybatis底层把参数封装为map,默认key为:list,map.put("list",Integer[])
    public int deleteUsersByIds2(List<Integer> rids);   
}
//deleted映射语句
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
    
   <delete id="deleteUserById" parameterType="Integer">
     delete from trole where r_id=#{rid}
    </delete>
        
    //批量删除,mybatis底层把参数封装为map,默认key为:array,map.put("array",Integer[])
    //item可随意输入  只要对应#{ccc}
    <delete id="deleteUsersByIds">
        delete  from trole where r_id in
        <foreach collection="rids" open="(" close=")" separator="," item="ccc">
            #{ccc}
        </foreach>
    </delete>
       
    //批量删除mybatis底层把参数封装为map,默认key为:list,map.put("list",Integer[])
    //item可随意输入  只要对应#{ccc}
    <delete id="deleteUsersByIds2">
        delete  from trole where r_id in
        <foreach collection="list" open="(" close=")" separator="," item="ccc">
            #{ccc}
        </foreach>
    </delete>
</mapper>  
public class TestDelete {
    private static SqlSessionFactory factory = null;
    private SqlSession s = null;

    @BeforeClass
    public static void init(){
        factory = SessionFactoryUtil.getFactory();
    }

    @Before
    public void oppension(){
        s = factory.openSession();
    }


    @Test
    public void test1_deleteUserById(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        int i = mapper.deleteUserById(9);
        s.commit();
        System.out.println(i);
    }

    @Test
    public void test1_deleteUserByIds(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        Integer[] rids = new  Integer[]{10,11,12};
        int i = mapper.deleteUsersByIds(rids);
        s.commit();
        System.out.println(i);
    }

    @Test
    public void test1_deleteUserByIds2(){
        BlogMapper mapper = s.getMapper(BlogMapper.class);
        List<Integer> rids = new ArrayList<>();
        rids.add(6);
        rids.add(7);
        int i = mapper.deleteUsersByIds2(rids);
        s.commit();
        System.out.println(i);
    }

    @After
    public void closeSession(){
        s.close();
    }

    @AfterClass
    public static void destory(){
        factory=null;
        System.out.println("destory");
    }
}

14.resultMap:结果集映射

实体类属性名与数据库的字段名不一致;
//column-数据库字段名    property-实体类属性名
<resultMap id="自定义" type="实体类别名">
    <id column="" property="">主键列映射
    <result column="" property=""> 非主键列映射e
<mapper namespace="com.javasm.sys.mapper.SysroleMapper">
    <!--把某列映射到某个类的某个成员变量-->
    <resultMap id="roleResultMap" type="sysrole">   
        <id column="rid" property="rid"></id><!--主键列映射-->
        <result column="role_name" property="rname"></result><!--非主键列映射-->
    </resultMap>

    <!--resultType:实体对象,Map,String,Integer,Double-->
    <!--resultMap:写的时resultMap标签的id-->
    <select id="selectRoleByID" resultMap="roleResultMap">
        select * from sysrole where rid = #{rid}
    </select>
        
</mapper>

15.collection,association对象关系映射

resultMap的子标签

关联查询:查询用户对象的时候,把用户的角色对象关联查询出来。

关联返回的List用collection 关联返回的对象用association

association: 多对一,一对一关系

​ 查询用户对象的时候,把用户的角色对象关联查询出来。


<mapper namespace="com.javasm.sys.mapper.SysuserMapper">
    //同名的可不加映射
    <resultMap id="userResultMap" type="Sysuser">
        <id column="uid" property="uid"></id>
        <result column="uname" property="uname"></result>
        <result column="rid" property="rid"></result>
        <!--association:column:外键列,javaType:成员变量的类型(类名),select:二次查询的位置-->
// 该实体类 加了关联的那个表的实体类对象          
//property-实体类属性名  javaType-属性名类型   column-下个跳转方法需要用到的id   select-跳转到哪个方法       
        <association property="srole" javaType="Sysrole" column="rid"  select="com.javasm.sys.mapper.SysroleMapper.selectRoleByID"></association>
    </resultMap>

 //这个同名也得加映射           
    <resultMap id="userResultMap2" type="Sysuser">
        <id column="uid" property="uid"></id>
        <result column="uname" property="uname"></result>
        <result column="upwd" property="upwd"></result>
        <result column="uphone" property="uphone"></result>
        <result column="uemail" property="uemail"></result>
        <result column="create_time" property="createTime"></result>
        <result column="update_time" property="updateTime"></result>
        <result column="rid" property="rid"></result>
        <!--association:column:外键列,javaType:成员变量的类型(类名)-->
        <association property="srole" javaType="Sysrole">
            //增加的那个关联的实体类属性  对应的映射
            <id column="rid" property="rid"></id>                    //主键
            <result column="role_name" property="rname"></result>    //需关联查询的内容
        </association>
    </resultMap>

    <select id="selectUserAndRoleByUserid" resultMap="userResultMap" parameterType="Integer">
        select * from sysuser where uid=#{uid}
    </select>

    <select id="selectUserAndRoleByUserid2" resultMap="userResultMap2" parameterType="Integer">
        select u.*,r.role_name from sysuser u,sysrole r where u.rid=r.rid and u.uid=#{uid}
    </select>

</mapper>
//三种方法   一对多关联查询
public class TestAssociaction {
    private static SqlSessionFactory factory =null;
    private SqlSession s =null;
    //junit测试方法
    @BeforeClass
    public static void init(){
        factory = SessionFactoryUtil.getFactory();
    }

    @Before
    public void opensession(){
        s = factory.openSession();
    }
    //查询用户对象的时候,把用户的角色对象关联查询出来。
    //手工发起两次查询。
    @Test
    public void test1_selectUserAndRoleByUserId(){
        SysuserMapper um = s.getMapper(SysuserMapper.class);
        SysroleMapper rm= s.getMapper(SysroleMapper.class);
        Sysuser sysuser = um.selectUserById(1);
        //通过查到的对象取到关联的id
        Integer rid = sysuser.getRid();
        //通过关联的id再次查询
        Sysrole sysrole = rm.selectRoleByID(rid);
        sysuser.setSrole(sysrole);
        System.out.println(sysuser);
    }

    //mybatis内部发起二次查询,需要通过ResultMap的标签的association关系标签来指定二次查询的位置
    @Test
    public void test2_selectUserAndRoleByUserId(){
        SysuserMapper um = s.getMapper(SysuserMapper.class);
        Sysuser sysuser = um.selectUserAndRoleByUserid(1);
        System.out.println(sysuser);
    }

    //sql语句使用表关联查询,一次查询把所有结果查询出来,然后把结果列映射到不同的对象中      (不推荐)
    @Test
    public void test3_selectUserAndRoleByUserId(){
        SysuserMapper um = s.getMapper(SysuserMapper.class);
        Sysuser sysuser = um.selectUserAndRoleByUserid2(1);
        System.out.println(sysuser);
    }

    @After
    public void closeSession(){
        s.close();
    }

    @AfterClass
    public static void destory(){
        factory=null;
        System.out.println("destory");
    }
}
collection:一对多 多对多

​ 查询角色对象的时候,把该角色下的所有用户查询出来。

//实体类属性表
public class Sysrole {
    private Integer rid;
    private String rname;
    private List<Sysuser> userList;//角色对象聚合了用户对象
}    
public interface SysroleMapper {
    public Sysrole selectRoleByID(Integer rid);//单查询角色对象
    public Sysrole selectRoleAndUsersByRID(Integer rid);//查询角色对象以及该角色下的用户集合
    public Sysrole selectRoleAndUsersByRID2(Integer rid);//查询角色对象以及该角色下的用户集合
}
<?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="com.javasm.sys.mapper.SysroleMapper">
    
    <!--把某列映射到某个类的某个成员变量-->
    <resultMap id="roleResultMap" type="sysrole">
        <id column="rid" property="rid"></id><!--主键列映射-->
        <result column="role_name" property="rname"></result><!--非主键列映射-->
    </resultMap>
    

    <resultMap id="roleAndUsersResultMap" type="sysrole">
        <id column="rid" property="rid"></id><!--主键列映射-->
        <result column="role_name" property="rname"></result><!--非主键列映射-->
        <!--ofType:集合的泛型类型,-->
        <collection property="userList" column="rid" ofType="Sysuser" select="com.javasm.sys.mapper.SysuserMapper.selectUsersByRoleid"></collection>
    </resultMap>

    <resultMap id="roleAndUsersResultMap2" type="sysrole">
        <id column="rid" property="rid"></id><!--主键列映射-->
        <result column="role_name" property="rname"></result><!--非主键列映射-->
        <!--property:实体类新增的那个集合对象名称--> 
        <!--ofType:集合的泛型类型,-->  
        <collection property="userList" ofType="Sysuser">
            <id column="uid" property="uid"></id>
            <result column="uname" property="uname"></result>
            <result column="upwd" property="upwd"></result>
            <result column="uphone" property="uphone"></result>
            <result column="uemail" property="uemail"></result>
            <result column="create_time" property="createTime"></result>
            <result column="update_time" property="updateTime"></result>
        </collection>
    </resultMap>

    <!--resultType:实体对象,Map,String,Integer,Double-->
    <!--resultMap:写的时resultMap标签的id-->
        
    <select id="selectRoleByID" resultMap="roleResultMap">
        select * from sysrole where rid = #{rid}
    </select>

    <select id="selectRoleAndUsersByRID" parameterType="int" resultMap="roleAndUsersResultMap">
      select * from sysrole where rid = #{rid}
    </select>

    <select id="selectRoleAndUsersByRID2" parameterType="int" resultMap="roleAndUsersResultMap2">
      select u.*,r.role_name from sysuser u,sysrole r where u.rid=r.rid and r.rid=#{rid}
    </select>
</mapper>
public class TestCollection {

    private static SqlSessionFactory factory =null;
    private SqlSession s =null;
    //junit测试方法
    @BeforeClass
    public static void init(){
        factory = SessionFactoryUtil.getFactory();
    }

    @Before
    public void opensession(){
        s = factory.openSession();
    }

    //建议使用这种,手工调用两个mapper对象中的方法进行单表查询
    @Test
    public void test1_selectRoleAndUsersByRoleId(){
        int rid= 1;
        SysroleMapper mapper = s.getMapper(SysroleMapper.class);
        SysuserMapper um = s.getMapper(SysuserMapper.class);
        Sysrole sysrole = mapper.selectRoleByID(rid);
        List<Sysuser> userList = um.selectUsersByRoleid(rid);
        sysrole.setUserList(userList);
        System.out.println(sysrole);
    }

    //mybatis的级联查询
    @Test
    public void test2_selectRoleAndUsersByRoleId(){
        int rid= 1;
        SysroleMapper mapper = s.getMapper(SysroleMapper.class);
        Sysrole sysrole = mapper.selectRoleAndUsersByRID(rid);
        System.out.println(sysrole);
    }

    //基于数据库表的级联查询。    (不推荐)
    @Test
    public void test3_selectRoleAndUsersByRoleId(){
        int rid= 1;
        SysroleMapper mapper = s.getMapper(SysroleMapper.class);
        Sysrole sysrole = mapper.selectRoleAndUsersByRID2(rid);
        System.out.println(sysrole);
    }

    @After
    public void closeSession(){
        s.close();
    }

    @AfterClass
    public static void destory(){
        factory=null;
        System.out.println("destory");
    }
}

16.动态代理

/**
 * 代理模式:
 *  被代理对象:原生已存在的对象,需要扩展的对象
 *  代理对象:需要我们创建的新对象,静态代理:手工创建代理类;动态代理:在程序运行期间,jvm内部动态的创建的代理类$Proxy0.java,编译为.class,实例化返回代理对象
 *
 *
 *  两个特征:代理类与被代理类同类型;代理类持有被代理类。
 *
 */
 
 //我理解就是扩展的方法在  public class MyInvocationHandler implements InvocationHandler 重写后
 //然后在在程序运行期间,jvm内部动态的创建的代理类$Proxy0.java,编译为.class,实例化返回代理对象
public class TestProxy {
    public static void main(String[] args) throws Exception {
        //设置虚拟机参数
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        //1.原生代理
        Class.forName("com.mysql.jdbc.Driver");
        Connection source = DriverManager.getConnection("jdbc:mysql://localhost:3306/720a", "root", "root");

        ClassLoader classLoader = source.getClass().getClassLoader(); //第一个参数 classLoader对象
        Class[] clz = {Connection.class};                             //第二个参数 被代理类类型
        InvocationHandler handler = new MyInvocationHandler(source);  //代理类的方法  包括修改过完年未修改的

        //o即是$Proxy0的实例化对象
        Connection o = (Connection) Proxy.newProxyInstance(classLoader, clz, handler);
        PreparedStatement pst = o.prepareStatement("select * from sysuser");//调用toString,其实是调用该对象InvocationHandler的invoke方法
        ResultSet resultSet = pst.executeQuery();
        resultSet.next();
        String uname = resultSet.getString("uname");
        System.out.println(uname);
        o.close();
    }
}
public class MyInvocationHandler implements InvocationHandler {
    private Object source = null;

    public MyInvocationHandler(Object source){
        this.source = source;
    }

    //代理对象,调用的方法对象
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        System.out.println(method.getName());
//      System.out.println(Arrays.toString(args));
        //重写close,其他方法放行继续调用被代理对象的方法
        if("close".equals(method.getName())){
            //放回连接池
            System.out.println("放回连接池");
            return null;
        }else{
            Object invoke = method.invoke(source, args);
            return invoke;
        }
    }
}

17.延迟加载

延迟加载:用的时候才去服务器端加载对应的数据,提高用户的响应速度
    mybatis的延迟加载不重要,重要的时延迟加载的思想(分页,针对前端界面的渲染图片,树形节点的加载,移动端数据列表的延迟加载)
    mybatis的延迟加载是针对于association与collection进行级联查询场景。    
    //一般级联才有延迟加载      
//mybatis-config
	<settings>
        
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启全局的延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"></setting>
        <!--积极加载改为消极加载--> 
        //value="true"时会在级联第二次调用的时候会把第二次的结果直接都查出来 
        //value="false"时会在级联第二次调用的时候只会查需要的那个
        <setting name="aggressiveLazyLoading" value="false"></setting> 
        <!-- 调用toString,equals不触发延迟对象的加载 -->
        //这个给 tostring 设置了延迟加载 自己去查文档 
        <setting name="lazyLoadTriggerMethods" value=""></setting>
        
    </settings>

        
  //Test
       
    @Test
    public void test1_lazyLoad(){
        SqlSessionFactory factory = SessionFactoryUtil.getFactory();
        SqlSession session = factory.openSession();

        SysuserMapper mapper = session.getMapper(SysuserMapper.class);
        Sysuser sysuser = mapper.selectUserAndRoleByUserid(1);
        System.out.println("-------------------------");
        System.out.println(sysuser.getSrole());     //懒加载设置后只有在这个调用的时候才会加载这个这个需求的信息
        session.close();
    }        
        

18.mybatis缓存

mybatis缓存:针对于查询数据.基于内存来存储数据。
    场景:查询用户id为1的数据,先去查询内存中的数据,查到的话则直接返回数据;查不到则执行sql查询数据库,把查询结果放到内存中。
    带来的问题:数据同步(缓存中的数据与数据库中的数据同步问题).
    同步问题解决:(删除或修改数据的时候,还是修改数据库,会把缓存中对应的数据清除)
    mybatis的缓存分为一级缓存(基于SqlSession进行缓存),二级缓存(sessionFactory级别)
        
    LRU – 最近最少使用的:移除最长时间不被使用的对象。 
    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。 
    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 
    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

缓存清理算法:
    先进先出
    最近最少命中
    最少命中    
//开启二级缓存
//映射文件里
    <cache eviction="LRU"
            flushInterval="600000"
            size="512"
            readOnly="true"/>
        
//Test
@Test
    public void test3_factoryCache(){
        SqlSessionFactory factory = SessionFactoryUtil.getFactory();
        SqlSession session = factory.openSession();
        SysuserMapper mapper = session.getMapper(SysuserMapper.class);//是一个代理对象,该代理对象从SysuserMapper派生。
        Sysuser sysuser = mapper.selectUserById(1);//先查SqlsesionFactory缓存(key-value),再查数据库
        System.out.println("------查询用户--------"+sysuser);
        session.close();

        SqlSession session2 = factory.openSession();
        SysuserMapper mapper2 = session2.getMapper(SysuserMapper.class);
        Sysuser sysuser2 = mapper2.selectUserById(1);
        System.out.println("------查询用户第二次--------"+sysuser2);
        session2.close();
    }        

19.了解getMapper

了解getMapper:
    内部对mapper接口创建代理对象。

注意点:

  • 以后建数据库表,起码create_time与update_time必须有,查询排序使用。
  • Date:日期类型。数据库使用timestamp类型,可以设置CURRENT_TIMESTAMP默认日期,可以设置根据时间戳更新(updatetime勾选上 )。
  • 实体类使用Date,String,建议使用String,如果使用Date到springMVC框架会带来麻烦。
  • 在命名(类名,成员变量名,包名,数据库字段名)的时候,不允许出现单个单词。
  • 在实体类,方法形参出现变量建议都使用包装类对象。
  • private String bName;首字母小写,紧跟字母大写(绝对不要这么写)
  • private String isOk;//不允许成员变量名is开头
  • 以后建表主键使用varchar,代码使用uid设置主键值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值