1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > Java-Mybatis(二): Mybatis配置解析 resultMap结果集映射 日志 分页 注解开发 Mybatis执行流程分析

Java-Mybatis(二): Mybatis配置解析 resultMap结果集映射 日志 分页 注解开发 Mybatis执行流程分析

时间:2023-08-03 05:54:38

相关推荐

Java-Mybatis(二): Mybatis配置解析 resultMap结果集映射 日志 分页 注解开发 Mybatis执行流程分析

Java-Mybatis-02

学习视频:B站 狂神说Java – /video/BV1NE411Q7Nx

学习资料:mybatis 参考文档 – /mybatis-3/zh/index.html

1、Mybatis 配置解析

1.1、核心配置文件

mybatis-comfig.xmlMybatis的配置文件包含了影响Mybatis行为的设置和属性信息。configuration(配置) properties(属性)settings(设置)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境配置) environment(环境变量) transactionManager(事务管理器)dataSource(数据源) databaseIdProvider(数据库厂商标识)mappers(映射器)

1.2、环境配置

资源配置环境。mybatis-config.xml 可以配置多个环境, 通过default去选择环境.

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境

通过default选择环境:

即:选择 development的资源配置环境 :

<environments default="development">

选择 test 的资源配置环境 :

<environments default="test">

MyBatis默认的事务管理器就是JDBC ,连接池:POOLED

对于事务管理器(transactionManager:在 MyBatis 中有两种类型的事务管理器(也就是type="[JDBC|MANAGED]"

<!--XML 配置文件或者预先定义的一个 config,去SqlSessionFactoryBuilder, 配置数据库,便于创建sqlSessionFactory实例--><configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/> <!--事务管理器--><dataSource type="POOLED"> <!--默认的连接池--><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment><environment id="test"><transactionManager type="JDBC"/> <!--事务管理器--><dataSource type="POOLED"> <!--默认的连接池--><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><!--关联映射文件--><mappers><mapper resource="com/AL/dao/UserMapper.xml"/></mappers></configuration>

1.3、属性 properties

我们可以通过properties属性来实现引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.poperties】

配置文件:db.properties:

driver = com.mysql.jdbc.Driverurl = jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8username = rootpassword = 123456

有了db.properties后,那么此时可以去简化 xml 资源配置文件。

注意:在xml资源配置文件中,需要注意这些参数的顺序 级别.所有的标签都规定了顺序.

顺序为:

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

<properties resource="db.properties"><!-- 在 db.properties 属性配置文件中定义了这些 sql 配置,可以不在这里的xml配置文件中定义。如果定义的话, 会以 xml资源配置文件中的 (引入外部配置文件) 为基准,优先级比较高<property name="username" value="root"/><property name="pwd" value="111"/>--></properties>

对于上面所示的在 mybatis-config.xml配置文件中 进行 引用 属性配置信息 db.properties的时候:

可以直接引入外部文件可以在其中增加一些属性配置如果两个文件有同一个字段,优先使用外部配置文件的

mybatis-config.xml 配置文件代码:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-////DTD Config 3.0//EN""/dtd/mybatis-3-config.dtd"><!--XML 配置文件或者预先定义的一个 config,去SqlSessionFactoryBuilder, 配置数据库,便于创建sqlSessionFactory实例--><configuration><properties resource="db.properties" /><!--可以给实体类起别名--><typeAliases><typeAlias type="com.AL.pojo.User" alias="User"/></typeAliases><environments default="development"> <!-- 定义默认的环境配置,使用default选择环境 development--><environment id="development"><transactionManager type="JDBC"/> <!--事务管理器--><dataSource type="POOLED"> <!--数据源、默认的连接池--><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment><environment id="test"><transactionManager type="JDBC"/> <!--事务管理器--><dataSource type="POOLED"> <!--默认的连接池--><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><!--关联映射文件--><mappers><mapper resource="com/AL/dao/UserMapper.xml"/></mappers></configuration>

注意:当配置文件是上述的db.properties:时候,此时运行测试发生了错误:

org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.sql.SQLNonTransientConnectionException: Cannot load connection class because of underlying exception: com.mysql.cj.exceptions.WrongArgumentException: Malformed database URL, failed to parse the connection string near ';useUnicode=true&amp;charsetEncoding=UTF-8'.

错误的意思是 数据库 url 格式错误,无法解析,在这个 “useUnicode=true” 附近。

将properties属性配置文件修改如下:

driver = com.mysql.jdbc.Driver#url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8#url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8url = jdbc:mysql://localhost:3306/mybatisusername = rootpassword = 123456

运行测试 test 的查询。发现可以正确查询,结果如下:

User{id=1, name='鑫仔', pwd='123456'}User{id=2, name='天啊', pwd='123456'}User{id=3, name='好胖', pwd='123890'}User{id=4, name='jack', pwd='123456'}User{id=5, name='clearlove', pwd='77777'}User{id=6, name='the shy', pwd='123456'}

属性properties 和 xml环境配置中的优先级

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

首先读取在 properties 元素体内指定的属性。然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

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

测试:

db.properties:

driver = com.mysql.jdbc.Driver#url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8url = jdbc:mysql://localhost:3306/mybatisusername = rootpassword = 123456

mybatis-config.xml:

<environments default="development"> <!-- 定义默认的环境配置,使用default选择环境 development--><environment id="development"><transactionManager type="JDBC"/> <!--事务管理器--><dataSource type="POOLED"> <!--数据源、默认的连接池--><property name="driver" value="${driver}"/><!--<property name="url" value="${url}"/>--><property name="url" value="jdbc:mysql://localhost:3306/jdbc"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment>

测试的结果为 jdbc数据库中的user 表:

User{id=1, name='烤羊腿', pwd='null'}User{id=3, name='烤鸭', pwd='null'}

1.4、类型别名

类型别名(typeAliases)

分析:

select * from user 等价于 select id,name,pwd from user;

mybatis会根据数据库的字段名去找对应的实体类的属性名,(他会将所有列名转换为小写,然后去找实体类中对应的 set方法 ,set方法后面的字段就对应数据库的字段名;如果不一样就会返回null)

类型别名可为 Java 类型设置一个短的名字。 它仅用于 XML 配置,意在降低冗余的完全限定类名书写。

例如:

在xml配置文件 mybatis-config.xml中 对实体类起别名:

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

此时我们需要在对应的mapper.xml中进行修改:返回的已经不是 resultType="com.AL.pojo.User"

<select id="getUserList" resultType="user">select * from mybatis.user</select>

测试:

public class UserDaoTest {@Testpublic void test(){//1.拿到sqlSessionFactory对象//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;List<User> userList = userMapper.getUserList(); //查询全部用户//获得结果集for (User user : userList) {System.out.println(user);}//关闭sqlSessionsqlSession.close();}}

结果为:

User{id=1, name='鑫仔', pwd='123456'}User{id=2, name='天啊', pwd='123456'}User{id=3, name='好胖', pwd='123890'}User{id=4, name='jack', pwd='978654321'}User{id=5, name='clearlove', pwd='77777'}

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases><package name="com.AL.pojo"/></typeAliases>

每一个在包 com.AL.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.AL.pojo.User 的别名为user;若有注解,则别名为其注解值。见下面的例子:

@Alias("hello")public class User {...}

此时的mapper.xml文件中的 映射返回类型修改:sql语句的修改

<select id="getUserList" resultType="hello">select * from mybatis.user</select>

在实体类少的时候,使用第一种; 在实体类多的时候,建议使用第二种。第一种直接对 类 进行别名设置,可以DIY; 在第二种时是对包中的Java Bean进行小写别名,如果DIY,需要自己在实体类中增加注解。

1.5、映射器(mappers)

关于SQL语句的映射方式:

MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 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><!-- 使用完全限定资源定位符(URL) --><mappers><mapper url="file:///var/mappers/AuthorMapper.xml"/><mapper url="file:///var/mappers/BlogMapper.xml"/><mapper url="file:///var/mappers/PostMapper.xml"/></mappers><!-- 使用映射器接口实现类的完全限定类名 --><mappers><mapper class="org.mybatis.builder.AuthorMapper"/><mapper class="org.mybatis.builder.BlogMapper"/><mapper class="org.mybatis.builder.PostMapper"/></mappers><!-- 将包内的映射器接口实现全部注册为映射器 --><mappers><package name="org.mybatis.builder"/></mappers>

自己的理解:

resources和java属于一个级别的,那么使用相对于类路径的资源引用就是

因为是资源引用,所以使用 resource,类路径吗,所以就是 com/AL/dao 这种路径的形式。

使用映射器接口实现类的完全限定类名

将包内的映射器接口实现全部注册为映射器:

MapperRegistry: 注册绑定我们的Mapper文件。

我们在xml配置文件中定义了 配置 congiguration ,能够去得到实例SqlSessionFactoryBuilder, 用它创建一个SqlSessionFactory

sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

定义我们的实体类 User, 和数据库表格相匹配;

工具类的创建,MybatisUtils中构建 SqlSessionFactory 实例,获得sqlSession

创建接口 UserMapper(SQL方法接口),利用mapper.xml文件完成(接口实现类) 接口方法的映射,sql 语句功能,这两者标签一一对应。 即id 和 接口中的方法名对应

在测试代码中 完成sqlSession 的调用测试,完成CRUD的具体参数传送,与mapper、mapper.xml 之间的方法 标签 一一对应。 因为就是去调用的接口实现类 sql中的方法,当然方法名一样了。

SQL映射语句的定义, 在 UserMapper.xml中完成了UserMapper 接口方法的实现。 而在mybatis-config.xml中的sql语句映射,即sql是对哪里的文件进行使用。

<mapper namespace="com.AL.dao.UserMapper"><select id="getUserList" resultType="com.AL.pojo.User">select * from mybatis.user</select>

或者是说, sql 文件中的数据进行调用的时候, 我们去哪里寻找这些 sql 语句。这时的办法是在配置文件 config中进行映射, 在这里你已经配置了数据库, 然后再加上数据库的 sql语句调用的 映射位置

添加映射文件时,同样要注意在配置中的标签顺序。

有以下几种方法:

方法一:使用相对于类路径的资源引用,调用资源文件 resource

<!--关联映射文件--><mappers><mapper resource="com/AL/dao/UserMapper.xml"/></mappers>

方法二:使用映射器接口实现类的完全限定名, 即使用class 文件去绑定注册

<!--使用映射器接口实现类的完全限定类名--><mappers><mapper class="com.AL.dao.UserMapper"/></mappers>

方式三:将包内的映射器接口实现全部注册为映射器直接将整个包进行绑定注册,映射

<!--将包内的映射器接口实现全部注册为映射器 --><mappers><package name="com.AL.dao"/></mappers>

注意点

接口和它的Mapper配置文件必须同名。 即接口和接口实现类(映射,sal语句) 文件名必须一致接口和它的Mapper配置文件必须在同一个包下!。 即接口和接口实现类在一个包里面

2、生命周期和作用域

Mybatis框架流程:

SqlSessionFactoryBuilder:

我们使用 SqlSessionFactoryBuilder的目的 就是为了去创建 SqlSessionFactory实例。构建实例完成后 就不需要了。 所以把它放在方法作用域, 在使用这个方法的时候 去进行调用,然后用完就可以丢了。得到 SqlSessionFactoryBuilder 是通过预定义 mybatis-config.xml 配置文件的。

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。最佳作用域是方法作用域(也就是局部方法变量)。

SqlSessionFactory:

SqlSessionFactory 数据库 session工厂, 这个的作用应该就类似于数据库连接池,连接和关闭数据库配置信息。有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。所以在你创建进行使用的时候 会一直伴随你这个应用结束关闭为止, 最佳作用域就是应用作用域(ApplocationContext)

可以把它想象为数据库连接池SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。SqlSessionFactory 的最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式。

SqlSession:

连接到 连接池的一个请求每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。用完之后应该赶快关闭,否则资源被占用。

每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它

这里面的每一个mapper,就代表一个事务

3、解决属性名和字段名不一致的问题

例子:

数据库中的字段:

实体类中的属性:

此时进行查询 id=1:

@Testpublic void getUserById(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User userById = mapper.getUserById(1);System.out.println(userById);sqlSession.close();}

结果为: 可以发现结果中,password为 null。 因为数据库中表格的字段为pwd,和实体类中的属性password不一致。

User{id=1, name='鑫仔', password='null'}

解决方法:针对属性名和字段名不一致的解决方法为:

3.1、改别名

即利用sql语句中 起别名的方式,使得pwd对应位 password。 如下所示,在mapper.xml中 改变sql语句,

<select id="getUserById" parameterType="int" resultType="com.AL.pojo.User"><!--select * from mybatis.user where id = #{id}-->select id, name, pwd as password from mybatis.user where id = #{id}</select>

3.2、结果集映射

resultMap:[推荐使用的方法]

类似于下面这样一一对应起来: 类似于前面使用的 万能map。

resultMap 元素是 MyBatis 中最重要最强大的元素。

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

<!--结果集映射 --><resultMap id="UserMap" type="User"><!--column数据库中的字段, property实体类中的属性--><result column="id" property="id"/><result column="name" property="name"/><result column="pwd" property="password"/></resultMap><select id="getUserById" resultMap="UserMap">select * from mybatis.user where id = #{id}</select>

ResultMap 的优秀之处——你完全可以不用显式地配置它们

不用显式地配置它们,需要哪一个,映射哪一个就可以了:

<!--结果集映射 --><resultMap id="UserMap" type="User"><!--column数据库中的字段, property实体类中的属性--><result column="pwd" property="password"/></resultMap>

对于属性名和字段名不一致的问题的 maven项目: 整个流程回顾:

3.2.1、此时创建的数据库为

CREATE DATABASE `mybatis`;USE `mybatis`;CREATE TABLE `user`(`id` INT(20) NOT NULL PRIMARY KEY,`name` VARCHAR(30) DEFAULT NULL,`pwd` VARCHAR(30) DEFAULT NULL)ENGINE=INNODB CHARSET=utf8;INSERT INTO `user`(`id`, `name`, `pwd`)VALUES (1,'鑫仔','123456'), (2,'天啊','123456'),(3,'好胖','123890')

3.2.2、mybatis-config.xml核心配置文件:

预先定义的一个 config,产生SqlSessionFactoryBuilder, 配置数据库以及连接,便于创建sqlSessionFactory实例,还需要有mappers即关联映射文件(SQL语句进行运行时去查找的 实体类,即对应着的数据库 表 字段名,就是你想要的那个 数据表):

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-////DTD Config 3.0//EN""/dtd/mybatis-3-config.dtd"><!--XML 配置文件或者预先定义的一个 config,去SqlSessionFactoryBuilder, 配置数据库,便于创建sqlSessionFactory实例--><configuration><properties resource="db.properties"><!-- 在 db.properties 属性配置文件中定义了这些 sql 配置,可以不在这里的xml配置文件中定义。如果定义的话, 会以 xml资源配置文件中的 (引入外部配置文件) 为基准,优先级比较高<property name="username" value="root"/><property name="pwd" value="111"/>--></properties><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><!--关联映射文件--><mappers><mapper resource="com/AL/dao/UserMapper.xml"/></mappers><!--使用映射器接口实现类的完全限定类名<mappers><mapper class="com.AL.dao.UserMapper"/></mappers>--><!--将包内的映射器接口实现全部注册为映射器<mappers><package name="com.AL.dao"/></mappers>--></configuration>

3.2.3、MybatisUtils工具类

创建这个sqlSessionFactory,是从资源文件resource(mybatis-config.xml)中去获得的。然后再从中获得 SqlSession 的实例

package com.AL.utils;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 java.io.InputStream;//import javax.annotation.Resources;// sqlSessionFactory --> sqlSessionpublic class MybatisUtils {// 创建这个sqlSessionFactory,是从资源文件resource中的去获得。private static SqlSessionFactory sqlSessionFactory;static {try {//利用mybatis第一步: 获取一个sqlSessionFactory对象String resource = "mybatis-config.xml";/** 注意:不需要修改,是因为导入的包不正确.不是 import javax.annotation.Resources* 将ResourcesgetResourceAsStream(resource);改为* Resources.class.getResourceAsStream(resource);* //得到配置文件流* InputStream inputStream = Resources.class.getResourceAsStream(resource);*/InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (Exception e) {e.printStackTrace();}}//有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。public static SqlSession getSqlSession() {//SqlSession sqlSession = sqlSessionFactory.openSession();//return sqlSession;return sqlSessionFactory.openSession();}}

3.2.4、实体类 User

对应着数据库表中的 user数据,不过此时的实体类User的属性名字和 数据库中的字段名不一致:pwd 与 password。

package com.AL.pojo;public class User {private int id;private String name;private String password;public User(int id, String name, String password) {this.id = id;this.name = name;this.password = password;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", password='" + password + '\'' +'}';}}

3.2.5、dao(Mapper)接口

userMapper接口

package com.AL.dao;import com.AL.pojo.User;import java.util.List;import java.util.Map;public interface UserMapper {// 获取全部的用户List<User> getUserList();// 根据ID查找用户. 利用 map 映射User getUserById2(Map<String,Object> map); // map}

userMapper.xml 接口实现类:完成映射,sql功能。 此时利用了resultMap 结果集映射,可以解决属性名和字段名不一致的问题。

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-////DTD Mapper 3.0//EN""/dtd/mybatis-3-mapper.dtd"><!--namespace= 绑定一个对应的Dao/Mapper接口<mapper namespace="org.mybatis.example.BlogMapper"><select id="selectBlog" resultType="Blog">select * from Blog where id = #{id}--><mapper namespace="com.AL.dao.UserMapper"><!--结果集映射 --><resultMap id="UserMap" type="com.AL.pojo.User"><!--一般通过id标签来映射主键column = 数据库的列名property = 结果集对应的数据库列名的映射名--><result column="id" property="id"/><result column="name" property="name"/><result column="pwd" property="password"/></resultMap><select id="getUserById2" resultMap="UserMap">select * from mybatis.user where id = #{id};</select><!--select 查询语句 --><select id="getUserList" resultType="com.AL.pojo.User">select * from mybatis.user</select></mapper>

3.2.6、测试

通过sqlSessionFactory对象,工具实体类, 拿到一个sqlSession实例。

通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象;

创建一个HashMap,来通过结果集映射去获得查询的结果

package com.AL.dao;import com.AL.pojo.User;import com.AL.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.HashMap;import java.util.List;public class UserDaoTest {@Testpublic void test(){//1.拿到sqlSessionFactory对象//SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSession();//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。//SqlSession sqlSession = sqlSessionFactory.openSession();SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userDao = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;List<User> userList = userDao.getUserList();//获得结果集for (User user : userList) {System.out.println(user);}//关闭sqlSessionsqlSession.close();}@Testpublic void getUserById2(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String, Object> map = new HashMap<String, Object>();map.put("id", 1);User userById2 = mapper.getUserById2(map);//获得结果System.out.println(userById2);sqlSession.close();}}

结果:

User{id=1, name='鑫仔', password='123456'}

4、日志

如果我们在进行数据库操作的时候,出现了异常,需要排错,检查错误。那么,此时,日志就是最好的助手

Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:

SLF4JApache Commons LoggingLog4j 2Log4jJDK loggingSTDOUT_LOGGING NO_LOGGING

MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。

你想在Mybatis中具体使用哪个日志实现,在设置中进行设定。设置名为 logImpl。

4.1、STDOUT_LOGGING

STDOUT_LOGGING:标准日志输出

注意细节:字体的大小写, 不能有空格

测试例子:在mybatis.xml文件中配置标准日志:注意此时setting 在configuration 中的顺序位置

<settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings>

输出结果为:

Opening JDBC Connection //打开JDBC连接Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.Created connection 1997859171. //创建连接Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7714e963]==> Preparing: select * from mybatis.user where id = ?==> Parameters: 5(Integer)<== Columns: id, name, pwd<== Row: 5, clearlove, 77777<==Total: 1User{id=5, name='clearlove', pwd='77777'} //寻找的结果Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7714e963]Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7714e963]Returned connection 1997859171 to pool. //关闭事务,连接池。

4.2、Log4j

Log4j:

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件等。 我们这里显示在控制台。我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

注意细节

字体的大小写, 不能有空格

测试例子1: 使用日志文件 Log4j

导入 Log4j的包

<!-- /artifact/log4j/log4j --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>

在mybatis-config.xml中配置 log4j 为日志的实现,注意此时setting 在configuration 中的顺序位置

<settings><setting name="logImpl" value="LOG4J"/></settings>

定义log4j.properties 资源配置文件:

```xml- ### Log4j配置 ####定义log4j的输出级别和输出目的地(目的地可以自定义名称,和后面的对应)#[ level ] , appenderName1 , appenderName2log4j.rootLogger=DEBUG,console,file#-----------------------------------##1 定义日志输出目的地为控制台log4j.appender.console = org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target = System.outlog4j.appender.console.Threshold=DEBUG####可以灵活地指定日志输出格式,下面一行是指定具体的格式 ####%c: 输出日志信息所属的类目,通常就是所在类的全名#%m: 输出代码中指定的消息,产生的日志具体信息#%n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行log4j.appender.console.layout = org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n#-----------------------------------##2 文件大小到达指定尺寸的时候产生一个新的文件log4j.appender.file = org.apache.log4j.RollingFileAppender#日志文件输出目录log4j.appender.file.File=log/info.log#定义文件最大大小log4j.appender.file.MaxFileSize=10mb###输出日志信息####最低级别log4j.appender.file.Threshold=ERRORlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#-----------------------------------##3 druidlog4j.logger.druid.sql=INFOlog4j.logger.druid.sql.DataSource=infolog4j.logger.druid.sql.Connection=infolog4j.logger.druid.sql.Statement=infolog4j.logger.druid.sql.ResultSet=info#4 mybatis 显示SQL语句部分.mybatis=DEBUG#.tibet.cas.dao=DEBUG#.mon.jdbc.SimpleDataSource=DEBUG#.mon.jdbc.ScriptRunner=DEBUG#.mybatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG#log4j.logger.java.sql.Connection=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG

测试: 注意,导入的包为 import org.apache.log4j.Logger;

package com.AL.dao;import com.AL.pojo.User;import com.AL.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.apache.log4j.Logger;import org.junit.Test;import java.util.List;public class UserDaoTest {static Logger logger = Logger.getLogger(UserDaoTest.class);@Testpublic void testLog4j(){logger.info("info: 进入了testLog4J");logger.debug("debug: 进入了testLog4J");logger.error("error: 进入了testLog4J");}@Testpublic void test(){//1.拿到sqlSessionFactory对象//SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSession();//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;List<User> userList = userMapper.getUserList(); //查询全部用户//获得结果集for (User user : userList) {System.out.println(user);}//关闭sqlSessionsqlSession.close();}@Test //插入public void addUser(){//1.拿到sqlSessionFactory对象//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;int res = userMapper.addUser(new User(4,"the shy","123456"));if (res>0){System.out.println("我插入成功了啊");}//提交事务//提交事务//提交事务mit();//关闭sqlSessionsqlSession.close();}

5、分页

在mysql中,已经学过有关分页的处理,使用 limit 关键字。

分页的主要作用:减少数据的处理量。

使用 Limit分页:语法如下所示:

//语法:SELECT * FROM user LIMIT stratIndex,pageSizeSELECT * FROM user LIMIT 0,2;SELECT * FROM user LIMIT 3; #[0,n]

在使用 SELECT * FROM user LIMIT 3; 时,会默认从0开始。

结果为:

1鑫仔1234562天啊1234563好胖123890

5.1、我们使用Mybatis来完成分页的功能

Dao接口:采用map映射

package com.AL.dao;import com.AL.pojo.User;import java.util.List;import java.util.Map;public interface UserMapper {// 获取全部的用户List<User> getUserList();//分页List<User> getUserLimit(Map<String, Integer> map);}

2.mapper映射,sql语句:

<select id="getUserLimit" parameterType="map" resultType="com.AL.pojo.User">select * from mybatis.user limit #{startIndex},#{pageSize}</select>

3.测试:

package com.AL.dao;import com.AL.pojo.User;import com.AL.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.apache.log4j.Logger;import org.junit.Test;import java.util.HashMap;import java.util.List;public class UserDaoTest {static Logger logger = Logger.getLogger(UserDaoTest.class);@Testpublic void testLog4j(){logger.info("info: 进入了testLog4J");logger.debug("debug: 进入了testLog4J");logger.error("error: 进入了testLog4J");}//分页@Testpublic void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("startIndex",1);map.put("pageSize",2);List<User> userLimit = mapper.getUserLimit(map);for (User user : userLimit) {System.out.println(user);}sqlSession.close();}@Testpublic void test(){//1.拿到sqlSessionFactory对象//SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSession();//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;List<User> userList = userMapper.getUserList(); //查询全部用户//获得结果集for (User user : userList) {System.out.println(user);}//关闭sqlSessionsqlSession.close();}}

分页的结果为:表格中索引的顺序从 0 开始。

User{id=2, name='天啊', pwd='123456'}User{id=3, name='好胖', pwd='123890'}

对应的如果将 limit 的数字修改:

map.put("startIndex",1);map.put("pageSize",1);

结果就是:User{id=2, name=‘天啊’, pwd=‘123456’}

5.2、RowBounds分页

1.接口:

public interface UserMapper {// 获取全部的用户List<User> getUserList();//分页List<User> getUserLimit(Map<String, Integer> map);//分页2List<User> getUserByRowBounds();}

2.接口实现类。 mapper.xml配置完成映射 sql语句

<mapper namespace="com.AL.dao.UserMapper"><!--select 查询语句 --><!--<select id="getUserList" resultType="com.AL.pojo.User">select * from mybatis.user</select>--><!--分页--><select id="getUserLimit" parameterType="map" resultType="com.AL.pojo.User">select * from mybatis.user limit #{startIndex},#{pageSize}</select><!--使用RowBounds分页--><select id="getUserByRowBounds" parameterType="map" resultType="com.AL.pojo.User">select * from mybatis.user</select><!--获得所有用户--><select id="getUserList" resultType="com.AL.pojo.User">select * from mybatis.user</select>

3.测试:使用java方法: 注意,在选择selection方法的时候,选择里面会有 参数类型为bounds的

package com.AL.dao;import com.AL.pojo.User;import com.AL.utils.MybatisUtils;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.session.SqlSession;import org.apache.log4j.Logger;import org.junit.Test;import java.util.HashMap;import java.util.List;public class UserDaoTest {static Logger logger = Logger.getLogger(UserDaoTest.class);@Testpublic void testLog4j(){logger.info("info: 进入了testLog4J");logger.debug("debug: 进入了testLog4J");logger.error("error: 进入了testLog4J");}@Testpublic void getUserByRowBounds(){SqlSession sqlSession = MybatisUtils.getSqlSession();// RowBounds 实现RowBounds rowBounds = new RowBounds(1, 2);List<User> userList = sqlSession.selectList("com.AL.dao.UserMapper.getUserByRowBounds",null,rowBounds);for (User user : userList) {System.out.println(user);}sqlSession.close();}@Testpublic void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("startIndex",1);map.put("pageSize",2);List<User> userLimit = mapper.getUserLimit(map);for (User user : userLimit) {System.out.println(user);}sqlSession.close();}@Testpublic void test(){//1.拿到sqlSessionFactory对象//SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSession();//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;List<User> userList = userMapper.getUserList(); //查询全部用户//获得结果集for (User user : userList) {System.out.println(user);}//关闭sqlSessionsqlSession.close();}}

结果:

User{id=2, name='天啊', pwd='123456'}User{id=3, name='好胖', pwd='123890'}

6、使用注解开发

6.1、面向接口编程

在开发中,会很多时候选择面向接口编程。原因:解耦,可拓展,提高复用。在分层开发中,上层不用管具体的实现,大家都会遵守共同的标准,使得开发变得容易,规范性更好。

在面向对象的系统中,系统的各种功能由不同的对象协助完成。此时,不会关注各自对象内部如何完成。 各个对象之间的协作称为系统设计的关键。不同模块之间的交互,这正是面向接口编程的思想。

三个面向的区别联系

面向过程:考虑问题时,以一个具体流程即 事务过程为单位,去实现它;面向对象:考虑问题时,以对象为单位,考虑它的属性及方法。各自对象内部采用具体的面向过程思想去解决面向接口:更多的体现在对系统整体的架构,不同模块之间的交互

抽象类和接口有什么不同?

抽象类即使用修饰符 abstract、 接口为 interface。

abstract修饰符即可以修饰方法也可以修饰类,修饰类的时候叫抽象类,修饰方法的时候叫做抽象方法。

不同点

抽象类中可以去定义构造函数,接口不能定义构造函数。(即有参构造器。无参构造器。总会默认存在无参构造器的)

抽象类中既可以有抽象方法,也可以有具体方法;接口中只能有抽象方法(在Java8中,接口可以带有具体实现的方法,使用default修饰,这类方法就是默认方法)

抽象类中的成员权限可以是public、default、protected抽象类中的抽象方法就是为了进行重写,所以不用 private进行修饰);接口中的成员只能是public方法默认就是:public abstract,成员变量默认:public static final,因为要被继承)

抽象类中可以包含静态方法,接口中不能。 但是在JDK1.8以后,接口中就能包括静态方法了。 原先不能原先是由于 接口不能实现方法(静态方法必须实现)。现在可以使用接口直接调用静态方法。

接口的成员变量默认是public static final。static定义后,将变量存储在静态区域,由于一个类中可以实现多个接口,所以为了防止重名,将其定义为static 存储在静态区域,一旦重复就会报错。【静态区域:方法区:常量池和 类变量】

之所以是 final,是因为接口被大家共同调用,它的变量不能随便更改。

JDK1.8中对接口增加了新的特性

(1)默认方法(default method):JDK 1.8允许给接口添加非抽象的方法实现,但必须使用default关键字修饰;定义了default的方法可以不被实现子类所实现,但只能被实现子类的对象调用;如果子类实现了多个接口,并且这些接口包含一样的默认方法,则子类必须重写默认方法;

(2)静态方法(static method):JDK 1.8中允许使用static关键字修饰一个方法,并提供实现,称为接口静态方法。接口静态方法只能通过接口调用(接口名.静态方法名)

相同点:

都不能被实例化。【即我们需要去完成接口实现类,抽象类的完成】可以将抽象类和接口作为引用类型一个类如果继承了某个抽象类或者实现了某个接口,就必须对其中所有的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。

什么时候该用接口什么时候该用抽象类

参考链接: /wb_snail/article/details/78862533

接口的本质是 多态。 定义的抽象方法在不同类中有不同的实现方式抽象类的本质是 多态 + 复用。 继承那么就有代码的复用性。 不用每次都再一次去实现一次抽象方法了。面向接口编程:那么自己遇到的最多的场景就是 MVC架构中的 业务层 service层。 接口+接口实现类即可抽象类编程:抽象类中的方法可以有实现的具体方法,那么这个方法 子类就可以直接继承后使用,它的复用性。抽象类的抽象方法,和接口一样,不同的子类对象去实现自己想要完成的具体形式,它的多态性。

抽象类就相当于一个模板,模板中有子类可以公用的部分,也有需要子类自行实现的部分,是为模板式设计,它同时具有多态性+复用性;

接口是对行为的抽象,它只定义一组行为规范,每个实现类都要实现所有规范,叫辐射式设计,它只有多态性;

抽象类是复用,可以有方法;接口是重写规范,但是也有默认(default)的方法(有方法体),不必重写这种方法

使用注解开发: 对于原来的mybatis。是需要使用xml配置的,完成映射,sql语句,接口实现类的工作。而注解,可以去代替一些xml的配置

例子:

1.接口的修改:且不要mapper.xml 映射配置文件。不需要接口实现类了。 直接注解开发,实现此sq功能。

public interface UserMapper {// 获取全部的用户@Select("select * from mybatis.user")List<User> getUserList();}

2.在resource里面的 mybatis-config.xml配置文件进行修改

既然原来的mapper.xml已经没有映射配置文件,那么这里的注册的 映射也不要,反而是进行绑定接口;

注意: 在这个mappers中进行关联映射文件,绑定接口时, 同一个接口名只能在此注册一次,不然会报错。 所以,我将原来的写为了注释。

<!--关联映射文件--><mappers><!--<mapper resource="com/AL/dao/UserMapper.xml"/>--><!--绑定接口--><mapper class="com.AL.dao.UserMapper"/></mappers>

3.测试。 获得sqlSession对象,然后对接口中的方法进行测试。

@Testpublic void getUser(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> user = mapper.getUser();for (User user1 : user) {System.out.println(user);}sqlSession.close();}

结果为:

[User{id=1, name='鑫仔', pwd='123456'}, User{id=2, name='天啊', pwd='123456'}, User{id=3, name='好胖', pwd='123890'}, User{id=4, name='jack', pwd='978654321'}, User{id=5, name='clearlove', pwd='77777'}]

对于注解开发:

本质: 利用的是 反射机制实现

底层: 动态代理

回顾:注解

来源链接:

https://mp./s?__biz=MzAxMjY1NTIxNA==&mid=2454441897&idx=1&sn=729688d470c94560c1e73e79f0c13adc&chksm=8c11e0a8bb6669be1cc4daee95b221ba437d536d598520d635fac4f18612dded58d6fddb0dce&scene=21#wechat_redirect

/yuzongtao/article/details/83306182

什么是注解?

注解也叫做元数据。例如@Override和@Deprecate(标明某个类或方法过时)。注解用于对代码的说明,可以去对包、类、接口、字段(属性把)、方法参数、局部变量等进行注解。可以分为三类:

Java自带的注解 即内置注解:@Override、@Deprecated、@SuppressWarnings元注解:用于对注解进行定义的注解。@Retention、@Target、@Inherited、自定义注解。自己定义名称。对于 注解类 的时候,前面的修饰符为 @Interface。

注解的用途

在看注解的用途之前,有必要简单的介绍下XML和注解区别,

注解:是一种分散式的元数据,与源代码紧绑定。xml:是一种集中式的元数据,与源代码无绑定

当然网上存在各种XML与注解的辩论哪个更好,这里不作评论和介绍,主要介绍一下注解的主要用途:

生成文档,通过代码里标识的元数据生成javadoc文档。编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例

注解使用演示

这边总共定义了4个注解来演示注解的使用

定义一个可以注解在Class,interface,enum上的注解,定义一个可以注解在METHOD上的注解定义一个可以注解在FIELD上的注解定义一个可以注解在PARAMETER上的注解

具体代码如下:

/*** 定义一个可以注解在Class,interface,enum上的注解*/@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@interface MyAnTargetType {/*** 定义注解的一个元素 并给定默认值* @return*/String value() default "我是定义在类接口枚举类上的注解元素value的默认值";}/*** 定义一个可以注解在METHOD上的注解*/@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@interface MyAnTargetMethod {/*** 定义注解的一个元素 并给定默认值* @return*/String value() default "我是定义在方法上的注解元素value的默认值";}/*** 定义一个可以注解在FIELD上的注解*/@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@interface MyAnTargetField {/*** 定义注解的一个元素 并给定默认值* @return*/String value() default "我是定义在字段上的注解元素value的默认值";}/*** 定义一个可以注解在PARAMETER上的注解*/@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@interface MyAnTargetParameter {/*** 定义注解的一个元素 并给定默认值* @return*/String value() default "我是定义在参数上的注解元素value的默认值";}

编写一个测试处理类处理以上注解:

/*** 测试java注解类** @author zhangqh* @date 4月22日*/@MyAnTargetTypepublic class AnnotationTest {@MyAnTargetFieldprivate String field = "我是字段";@MyAnTargetMethod("测试方法")public void test(@MyAnTargetParameter String args) {System.out.println("参数值 === "+args);}public static void main(String[] args) {// 获取类上的注解MyAnTargetTypeMyAnTargetType t = AnnotationTest.class.getAnnotation(MyAnTargetType.class);System.out.println("类上的注解值 === "+t.value());MyAnTargetMethod tm = null;try {// 根据反射获取AnnotationTest类上的test方法Method method = AnnotationTest.class.getDeclaredMethod("test",String.class);// 获取方法上的注解MyAnTargetMethodtm = method.getAnnotation(MyAnTargetMethod.class);System.out.println("方法上的注解值 === "+tm.value());// 获取方法上的所有参数注解 循环所有注解找到MyAnTargetParameter注解Annotation[][] annotations = method.getParameterAnnotations();for(Annotation[] tt : annotations){for(Annotation t1:tt){if(t1 instanceof MyAnTargetParameter){System.out.println("参数上的注解值 === "+((MyAnTargetParameter) t1).value());}}}method.invoke(new AnnotationTest(), "改变默认参数");// 获取AnnotationTest类上字段field的注解MyAnTargetFieldMyAnTargetField fieldAn = AnnotationTest.class.getDeclaredField("field").getAnnotation(MyAnTargetField.class);System.out.println("字段上的注解值 === "+fieldAn.value());} catch (Exception e) {e.printStackTrace();}}}

运行的结果如下:

类上的注解值 === 我是定义在类接口枚举类上的注解元素value的默认值参数上的注解值 === 我是定义在参数上的注解元素value的默认值参数值 === 改变默认参数方法上的注解值 === 测试方法字段上的注解值 === 我是定义在字段上的注解元素value的默认值

反射

正常方式为: 引入需要的"包类"名称,即导入jar包 --> 通过new实例化 --> 取得实例化对象

反射方式为:实例化对象 --> getClass()方法 --> 得到完整的"包类"名称

原文链接:/weixin_42298270/article/details/113371164

反射机制:

Java反射机制是在运行状态中,对于任意一个类,都能够获取这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取类信息以及动态调用对象内容就称为Java语言的反射机制。

简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在Java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。【运行时加载,动态地创建你想要的实例

反射的实现:

要使用一个类,就要先把它加载到虚拟机中,生成一个Class对象。这个Class对象就保存了这个类的一切信息。反射机制的实现,就是获取这个Class对象,通过Class对象去访问类、对象的元数据以及运行时的数据。

1、获取Class对象的三种方式

使用 Class 类中的 forName()静态方法调用某个对象的 getClass()方法(需要new对象)调用某个类的 class 属性来获取该类对应的 Class 对象

2、创建Class对象的两种方法

Class 对象的 newInstance()。使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。调用 Constructor 对象的 newInstance()。先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。

Java中为什么要用反射机制?直接创建对象不就可以了吗,我觉得这主要涉及到了动态与静态的问题。

静态编译:在编译时确定类型,绑定对象,即通过。动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以**降低类之间的耦合性**。

反射的优点缺点

优点:可以实现动态创建对象和编译,体现出很大的灵活性。【因为你能获得这个类所有的信息】

缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是 慢于 直接执行相同的操作。

例子: 反射,反射中获取Class类的实例可以采用这种语法:Class c = Class.forName(“java.lang.String”)。 在下面提到了获取Class类的实例的几种方法。

在这里,我们创建了一个实体类 User, 然后利用反射去实例化对象。 并去观察这几个实例化对象的 hashCode是否一样。

一个类在内存中只有一个Class对象,类被加载后,它的整个结构都会被封装在 Class 对象中。

package com.AL.Reflection;//什么叫反射public class Test01 {public static void main(String[] args) throws ClassNotFoundException {Class c1 = Class.forName("com.AL.Reflection.User");System.out.println(c1);Class c2 = Class.forName("com.AL.Reflection.User");Class c3 = Class.forName("com.AL.Reflection.User");Class c4 = Class.forName("com.AL.Reflection.User");// 一个类在内存中只有一个class对象// 一个类被加载后,类的整个结构都被封装在class对象中System.out.println(c2.hashCode()); //观察hashCode是否一样System.out.println(c3.hashCode());System.out.println(c4.hashCode());}}// 实体类 表示:pojo, entityclass User{private String name;private int id;private int age;public User() {}public User(String name, int id, int age){this.name = name;this.id = id;this.age = age;}public String getName(){return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", id=" + id +", age=" + age +'}';}@Overridepublic int hashCode() {return super.hashCode();}}

例子:通过反射, 动态的创建对象

在这里,我们采用Class类的forName() 方法去获取一个 Class 类的实例, 包名+类名。

有了Class对象, 我们可以采用newInstance()方法去创建一个具体的实例化的对象。 注意:这种方法调用的是无参构造器,你创建的User类中默认会有一个无参构造器; 但是,如果你定义了有参构造器,默认的会失效,你必须显示的定义 添加一个无参构造器。

// 获得class对象Class c1= Class.forName("com.AL.Reflection.User");// 构造一个对象User user1 = (User)c1.newInstance(); // 本质是调用了类的无参构造器 // 需要在User类中添加一个无参构造器System.out.println(user1);

当不使用无参构造器时, 我们可以采用getDeclaredConstructor() 方法去构建这个类中(此时为User类)的构造器, 需要在 () 这里面指定类中的形参类型。然后可以向构造器传递形参的具体元素数值。且这里是通过 Constructor 实例化对象

Constructor constructor= c1.getDeclaredConstructor(String.class, int.class, int.class);User user2 = (User)constructor.newInstance("ALZN",001,10);

通过反射, 动态的创建对象例子完整代码:

package com.AL.Reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;// 动态的创建对象, 通过反射public class Test08 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException, InstantiationException {// 获得class对象Class c1= Class.forName("com.AL.Reflection.User");// 构造一个对象User user1 = (User)c1.newInstance(); // 本质是调用了类的无参构造器 // 需要在User类中添加一个无参构造器System.out.println(user1);// 通过构造器创建对象Constructor constructor= c1.getDeclaredConstructor(String.class, int.class, int.class);User user2 = (User)constructor.newInstance("ALZN",001,10);System.out.println(user2);//通过反射调用普通方法User user3 = (User)c1.newInstance();//通过反射类获取一个方法Method setName = c1.getDeclaredMethod("setName", String.class);// invoke: 激活的意思// (对象, "方法的值")setName.invoke(user3,"ALZN");System.out.println(user3.getName());//通过反射操作属性System.out.println("##################");User user4 = (User)c1.newInstance();Field name = c1.getDeclaredField("name");//不能直接操作私有属性, 我们需要关闭安全检测,属性或方法的 setAccessible(true)name.setAccessible(true);name.set(user4,"ALZNZN");System.out.println(user4.getName());}}

结果:

User{name='null', id=0, age=0}User{name='ALZN', id=1, age=10}ALZN##################ALZNZN

注意:

通过反射调用方法时,需要激活该方法:

//通过反射调用普通方法User user3 = (User)c1.newInstance();//通过反射类获取一个方法Method setName = c1.getDeclaredMethod("setName", String.class);// invoke: 激活的意思 (对象, "方法的值")setName.invoke(user3,"ALZN");System.out.println(user3.getName());

通过反射操作私有属性时,需要关闭安全检测:

//通过反射操作属性User user4 = (User)c1.newInstance();Field name = c1.getDeclaredField("name");//不能直接操作私有属性, 我们需要关闭安全检测,属性或方法的 setAccessible(true)name.setAccessible(true);name.set(user4,"ALZNZN");System.out.println(user4.getName());

反射操作注解

反射操作注解:

getAnnotationsgetAnnotation

了解一下ORM, 即Object relationship Mapping 对象关系映射

我们后面在 数据库 表 和 java的实体类中 属性对应起来:

类和表的结构对应起来。即类 class Student 和这个数据库中的表 student 名字对应属性和字段对应。 类中的 属性 id name age 和表中的字段对应起来。对象和记录对应。 实例化对象后的数据信息 和 表中的记录 即表里面的信息 对应起来。

反射操作注解的例子:

package com.AL.Reflection;import java.lang.annotation.*;import java.lang.reflect.Field;//反射操作注解public class Test12 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class c1 = Class.forName("com.AL.Reflection.Student2");//通过反射获得注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println(annotation);}//获得注解的value值TableNote tableNote = (TableNote) c1.getAnnotation(TableNote.class);System.out.println(tableNote.value());//获得并打印注解的值//获得类指定的注解Field name = c1.getDeclaredField("name");//要获得的类型FieldNote annotation = name.getAnnotation(FieldNote.class);//获取值的方法System.out.println(annotation.columnName()+annotation.type()+annotation.length());}}@TableNote("db_student")class Student2{@FieldNote(columnName = "db_id",type = "int",length = 10)private int id;@FieldNote(columnName = "db_age",type = "int",length = 10)private int age;@FieldNote(columnName = "db_name",type = "String",length = 3)private String name;public Student2() {}public Student2(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student2{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';}}//类注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface TableNote{String value();}//属性的注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface FieldNote{String columnName();String type();int length();}

结果:

@com.AL.Reflection.TableNote(value="db_student")db_studentdb_nameString3

自己理解的 注解和反射的关系:

使用注解,无论是java中的自带标准注解 @Override 重写 还是利用元注解以及自定义注解,去创建一个注解的类。对这个类使用注解进行开发,指定这个注解对象能够修饰的范围、存在的作用域,当然这个里面的属性、方法是自己定义或者实现的。 我们使用注解 让这个注解类 具有它的意义、属性值。

注解开发是什么呢? 其实就是利用反射机制获取注解类中的 注解信息,然后也能利用反射去获取或者 自己定义属性信息。 这样的话 就能解释 @Bean的作用。

@Bean 利用注解开发,反射机制去获取这个类的配置信息,将这个类的实例 注入到容器Bean中。 这里面获取自己想要的类的 一个具体实例,就用到了 set()方法 属性注入, invoke()方法是反射中对方法的一个操作。

6.2、Mybatis执行流程分析

创建 sqlSessionFactory 对象的时候,java程序作了什么工作? 点击查看源码

build函数所做的事情:

在构造sqlSessionFactory实例化前的 build 会解析配置文件流xmlConfigBuilder,Configuration所有的配置信息。

从上面的这个图片也可以看出:mybatis的加载数据库资源的顺序为:

首先读取在 properties 元素体内指定的属性。然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

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

Mybatis详细执行流程

6.3、注解增删查改

我们可以在工具类创建的时候,实现自动提交事务。

使用注解进行 sql,并选择自动提交事务

1.在实体工具类。 mybatisUtiles 工具类修改为自动提交事务。 就不需要自己每次手动提交事务了。

注意:在sqlSessionFactory.openSession这个方法 选择其中参数带有 boolean的。

package com.AL.utils;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 java.io.InputStream;//import javax.annotation.Resources;// sqlSessionFactory --> sqlSessionpublic class MybatisUtils {// 创建这个sqlSessionFactory,是从资源文件resource中的去获得。private static SqlSessionFactory sqlSessionFactory;static {try {//利用mybatis第一步: 获取一个sqlSessionFactory对象String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (Exception e) {e.printStackTrace();}}//有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。public static SqlSession getSqlSession() {//return sqlSessionFactory.openSession();return sqlSessionFactory.openSession(true); //在sqlSessionFactory.openSession这个方法 选择其中参数带有 boolean的。}}

2.接口 mapper文件的修改。在接口中使用注解完成sql语句,实现该方法下的功能

方法中存在多个参数时,所有的参数前面必须添加 @Param(“id”) 注解

注意:在这里我要使用注解开发完成 mybatis中的 CRUD, 所以 Mapper.xml 映射文件应该删除。 不然这个xml映射文件中的接口实现类的方法对应的 id 名即方法会和注解此时能够完成的方法名发生冲突,导致在测试中发生错误。

错误类似于:Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for

CRUD代码:

package com.AL.dao;import com.AL.pojo.User;import org.apache.ibatis.annotations.*;import java.util.List;import java.util.Map;public interface UserMapper {//注解开发@Select("select * from mybatis.user")List<User> getUsers();// 根据ID查找用户// 方法中存在多个参数时,所有的参数前面必须添加 @Param("id") 注解@Select("select * from mybatis.user where id = #{id}")User getUserById(@Param("id") int id); //依据的是 id,所以用 int id//insert 一个用户@Insert("insert into user(id,name,pwd) values (#{id}, #{name}, #{pwd})")int addUser(User user); //插入一个用户,即插入了一个具体的实例,对象,所以用 user//修改一个用户@Update("update user set name=#{name}, pwd=#{pwd} where id = #{id}")int updateUser(User user);//删除一个用户@Delete("delete from user where id = #{id}")int deleteUser(int id);}

3.测试:此时的增删改事务,不用自己手动进行提交事务 就可以完成。

package com.AL.dao;import com.AL.pojo.User;import com.AL.utils.MybatisUtils;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.session.SqlSession;import org.apache.log4j.Logger;import org.junit.Test;import java.util.HashMap;import java.util.List;public class UserDaoTest {static Logger logger = Logger.getLogger(UserDaoTest.class);@Testpublic void testLog4j(){logger.info("info: 进入了testLog4J");logger.debug("debug: 进入了testLog4J");logger.error("error: 进入了testLog4J");}@Testpublic void getUsers(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> user = mapper.getUsers();for (User user1 : user) {System.out.println(user);}sqlSession.close();}@Test //查询public void getUserById(){//1.拿到sqlSessionFactory对象//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;User userById = userMapper.getUserById(2);//获得结果System.out.println(userById);//关闭sqlSession}@Test //插入public void addUser(){//1.拿到sqlSessionFactory对象//2.通过sqlSessionFactory对象openSession()创建一个sqlSession。SqlSession sqlSession = MybatisUtils.getSqlSession();//3.通过sqlSession获得mapper对象 , 参数为映射文件对应的接口类的class对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//4.通过mapper对象来执行操作;int res = userMapper.addUser(new User(4,"jack","123456"));if (res>0){System.out.println("我插入成功了啊");}//提交事务//提交事务//提交事务mit();//关闭sqlSession}@Test //更新public void updateUser(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.updateUser(new User(4, "jack", "321"));mit(); //提交事务}@Test //删除public void deleteUser(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.deleteUser(4);mit(); //提交事务}}

其中某个部分的结果:查看数据库的 user 表为

1鑫仔1234562天啊1234563好胖1238904jack1234565clearlove777776the shy123456

注意点

在mybatis中,无论是用 mapper.xml接口实现类,还是注解开发 去完成接口中方法对应的 sql 功能,都需要在 核心配置文件 mybatis-config.xml中进行注册 绑定。

在之前没有使用注解,使用接口实现类时:绑定的 映射的 方法为:

<!--关联映射文件--><mappers><mapper resource="com/AL/dao/UserMapper.xml"/></mappers>

使用注解开发,进行绑定的接口类为:

<mappers><!--resource 对应的是一个接口类的映射文件--><!--class 绑定接口,对应的是一个接口类--><mapper class="com.AL.dao.UserMapper"/></mappers>

接口和它的Mapper配置文件必须同名接口和它的Mapper配置文件必须要在同一个包下。

关于@Param

@Param注解用于给方法参数起一个名字。以下是总结的使用原则:

在方法只接受一个参数的情况下,可以不使用@Param。在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。如果参数是 JavaBean , 则不能使用@Param。不使用@Param注解时,参数只能有一个,并且是Javabean。

原来的SQL语句:

delete fromups_role_permission_dataparamswhere role_id = #{roleId, jdbcType=INTEGER}

在这里用到了 #{}。 使用 #{} 时:

#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符?【推荐使用】

INSERT INTO user (name) VALUES (#{name});INSERT INTO user (name) VALUES (?);

用来传入参数,sql在解析的时候会加上 " " 去当成字符串来解析,例如这里的 role_id = "roleid "#{} 能够在很大程度上防止 SQL注入

${} 的作用是直接进行字符串替换

INSERT INTO user (name) VALUES ('${name}');INSERT INTO user (name) VALUES ('kuangshen');

延伸:

在用 ${} 去传入数据是直接显示在生成的 sql中, 比如 role_id = #{roleId, jdbcType=INTEGER},在 sql中会被解析成 role_id = roleid,执行时会报错${} 方式无法防止sql注入$ 一般用入传入数据库对象, 比如数据库表名能用 #{} 就用 #{}。

注意:Mybatis中进行排序时使用order by动态参数时注意,使用的是 ${} 而不是用 #{}

7、Lombok的使用

Lombok的准备工作

在IDEA中安装Lombok插件

打开file->setting, ->Plugins,然后搜索 Lombok,进行安装。如下所示:

maven导入依赖, jar包:

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>

例子: 使用Lombok这个包,然后可以使用注解开发。如下所示:

在一个实体类 User中 使用Data 注解,可以得到一系列 get/set、toString 等多种方法。

package com.AL.pojo;import lombok.Data;@Datapublic class User {private int id;private String name;private String pwd;}`

结果:可以观察到 多的这些方法:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。