Mybatis原理及应用 Configuration

2022-08-18 10:58:56

JDBC(Java DataBase Connectivity)

JPA:Java persistence api:,即Java的持久化API,数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称. 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘).

在Java中,数据库存取技术只能通过JDBC访问数据库,JDBC本身是java连接数据库的一个标准,是进行数据库连接的抽象层,由java提供的一组类和接口组成,接口的实现由各个数据库厂商来完成–即数据库驱动包.

JDBC的类/接口全部引入的是java.sql包/javax.sql包

//mysql-connector-java 是MySQL的JDBC驱动包,用JDBC连接MySQL数据库时必须使用该jar包<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>

数据库连接:通过SPI机制加载数据库驱动,比如mysql驱动com.mysql.jdbc.Driver

//通过加载properties获取连接对象参数值
ClassLoader loader=...;//getResourceAsStream获取根目录下资源
InputStream in= loader.getResourceAsStream("db.properties");
Properties properties=newProperties();
properties.load(in);//获取连接对象的三要素
url= properties.getProperty("url");
username= properties.getProperty("username");
password= properties.getProperty("password");//加载mysql驱动
Class.forName("com.mysql.jdbc.Driver");//参数为数据库连接3要素:地址,用户名和密码@Cleanup//lombok提供的自动关闭资源
Connection connection= DriverManager.getConnection(url,username,password);

Statement:SQL语句对象,最终与数据库进行交互的实例

可以理解为一个db请求,将需要执行的sql语句请求到授信的db中,db执行完将结果响应回去

//构建语句对象@Cleanup
Statement statement= connection.createStatement();//执行的静态SQL
String sqlDml="INSERT INTO `test_jdbc` (`id`, `test_name`) VALUES ('3', 'good1')";
String sqlDql="SELECT * FROM `test_jdbc`";//DML操作,DDL(建表等)语句固定返回 0,DML(增删改)语句返回受影响的行数int update= statement.executeUpdate(sqlDml);//DQL操作,查询数据,执行查询的SQL语句返回结果集对象@Cleanup
ResultSet resultSet= statement.executeQuery(sqlDql);while(resultSet.next()){//获取第1列的值
    Object object= resultSet.getObject(1);//获取指定列的值
    Object object1= resultSet.getObject("name");}

PreparedStatement:预编译语句对象,执行动态SQL,sql语句本身就是静态的,所以早期会存在sql注入的风险,所以使用预编译语句对象设置参数值来避免此类问题

Statement处理sql就是直接进行的字符串拼接,字符串需要加上引号,而PreparedStatement是通过参数替换,也就是索引占位符,转换为?再设置对应的参数

//执行静态SQL
String sqlDql="SELECT * FROM `test_jdbc` WHERE `id` = 1  AND `test_name` = 'sad' ";//SQL注入
sqlDql= sqlDql+" OR 1=1 OR ''";

prepareStatement预编译就是在第一次执行SQL前数据库会进行分析、编译和优化,同时执行计划同样会被缓存起来,就是将格式固定的SQL请求到数据库进行编译,存放在缓冲区中,当再次执行相同的SQL语句时就不需要预编译的过程,直接使用编译好的SQL,替换占位符?,因为SQL注入只能对编译过程起作用,这样的方式就避免了SQL注入的问题

//获取预编译语句对象@Cleanup
PreparedStatement ps= connection.prepareStatement(sqlDql);//动态SQL
String sqlDql="SELECT * FROM `test_jdbc` WHERE `id` = ?";//设置占位符参数,给第index个参数设置值,index的值是从1开始
ps.setLong(1,3L);//DQL操作,查询数据没有参数@Cleanup
ResultSet resultSet= ps.executeQuery();while(resultSet.next()){
    Object object= resultSet.getObject(2);}

结果集映射:使用内省和反射的方式将结果集映射为对象结果集

private List<Student>dql(String sqlDql,Object...params)throws Exception{@Cleanup
    Connection connection= JDBCUtil.getConnection();@Cleanup
    PreparedStatement ps= connection.prepareStatement(sqlDql);for(int i=0;i<params.length;i++){
        ps.setObject(i+1,params[i]);}
    List<Student> list=newArrayList<>();
    ResultSet resultSet= ps.executeQuery();//内省方式获取属性,再通过反射设置
    Class<Student> stuClz= Student.class;
    PropertyDescriptor[] pds= Introspector.getBeanInfo(stuClz, Object.class).getPropertyDescriptors();while(resultSet.next()){
        Student student= stuClz.newInstance();for(PropertyDescriptor pd: pds){//WriteMethod就是set方法,字段要与结果集列名匹配
            pd.getWriteMethod().invoke(student,resultSet.getObject(pd.getName()));}
        list.add(student);}return list;}

jdbc事务:ACID属性,原子性(Atomicity)事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生;一致性(Consistency) 事务必须从一个一致性状态变换到另外一个一致性状态;隔离性(Isolation)指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰;持久性(Durability)事务一旦被提交,它对数据库中数据的改变是持久的

try{//设置事务的手动提交
    connection.setAutoCommit(false);
    操作1;
    操作n;//提交事务
    connection.commit();}catch(Exception e){//回滚事务
    connection对象.rollback();}

autoGeneratedKeys:插入操作时获取自动生成的主键,在创建预编译语句对象的时候设置返回主键,在插入操作时从返回的结果集获取到自动生成的主键

//设置返回主键 Statement.RETURN_GENERATED_KEYS = 1@Cleanup//prepareStatement(String sql, int autoGeneratedKeys)
PreparedStatement ps= connection.prepareStatement(sqlDml,1);
ps.setString(1,student.getName());
ps.executeUpdate();//获取主键
ResultSet resultSet= ps.getGeneratedKeys();if(resultSet.next()){long id= resultSet.getLong(1);long id1= resultSet.getLong("GENERATED_KEY");}

DataSource:数据库连接池,访问数据库都要先建立数据库连接,连接池由javax.sql包提供的数据池接口,由其他厂商提供实现

//未使用连接池
Connection conn= DriverManager.getConnection(url,username,password);//使用了连接池
Connection conn= dataSource.getConnection();

dbcp连接池

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-dbcp2</artifactId><version>2.0</version></dependency>

BasicDataSource dataSource=newBasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jdbc-demo");
dataSource.setUsername("root");
dataSource.setPassword("admin");
Connection connection= dataSource.getConnection();
Statement statement= connection.createStatement();
ResultSet resultSet= statement.executeQuery("select * from student where id =3");if(resultSet.next()){
    System.out.println(resultSet.getLong(1));}

druid连接池

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency>//本质上也是 DruidDataSource dataSource = new DruidDataSource();//然后将配置项set到dataSource中config(dataSource, properties);
DataSource dataSource= DruidDataSourceFactory.createDataSource(properties);
dataSource.getConnection();

自定义连接池

publicclassMyDataSoureceimplementsDataSource{//已经创建的连接实例集合publicstatic LinkedList<Connection> connections= Lists.newLinkedList();//默认连接池大小publicstaticint poolSize=20;//初始化创建好固定数量的连接池static{for(int i=poolSize;i>0;i--){
            Connection connection= DriverManager.getConnection("连接参数");
            connections.add(connection);}}@Overridepublic ConnectiongetConnection()throws SQLException{if(connections.size()>0){//获取连接池将按顺序移出,当使用完时再add到集合中即可return connections.removeFirst();}return null;}}

SqlSessionFactory

mybatis是一种比较流行的映射框架,基于MyBatis3主要了解一下mybatis的一些核心组件,比如SqlSession,Configuration,Executor等;从执行流程上来看,首先是通过配置项(包括DataSource连接实例)解析构建一个Configuration,再构建成一个SqlSessionFactory,调用openSession()获取到一个连接会话实例SqlSession,从而通过aop生成数据交互层代理类Mapper完成交互操作

@SpringBootApplication@MapperScan(basePackages={"com.mytest.mybatis.mapper"})publicclassApplication{}

SqlSessionFactory用于创建操作数据库交互的会话实例SqlSession,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得,而 SqlSessionFactoryBuilder 则可以从配置项比如 XML 配置文件或通过Java的方式构建;一个SqlSessionFactory对应一个配置环境(environment),也就是一个Configuration实例,如果要使用多个数据库交互就需要配置多个环境对应的SqlSessionFactory

//只需要建立一次sqlSession连接即可
InputStream resource= Resources.getResourceAsStream("configuration.xml");//build方法参数可以是多个数据源渠道,比如properties或者map
SqlSessionFactory sqlSessionFactory=newSqlSessionFactoryBuilder().build(resource);//openSession参数:execType执行批量或者简单sql,level传播级别,autoCommit是否自动提交事务//也可以传入Connection,从而autoCommit由connection提供
SqlSession sqlSession= sqlSessionFactory.openSession();//sqlSession是应用程序与持久层之间执行交互操作的一个单线程实例
PersonMapper mapper= sqlSession.getMapper(PersonMapper.class);
mapper.deleteData();
sqlSession.commit();

通过SqlSessionFactoryBuilder创建SqlSessionFactory,在SqlSessionFactoryBuilder的build()方法中可以看到定义了一个XMLConfigBuilder用来解析xml配置文件

//通过XMLConfigBuilder解析配置,然后解析成Configuration实例public SqlSessionFactorybuild(InputStream inputStream, String environment, Properties properties){//xml配置构建器
    XMLConfigBuilder configBuilder=newXMLConfigBuilder(inputStream, environment, properties);//parese()方法解析xml返回一个Configuration实例,根据configuration构建sqlSessionFactory
    SqlSessionFactory var5=this.build(configBuilder.parse());}//如果是手动装配则需要解析xmlpublic Configurationparse(){
    parsed=true;//xml配置项的的根节点就是<configuration>parseConfiguration(parser.evalNode("/configuration"));return configuration;}//默认是DefaultSqlSessionFactorypublic SqlSessionFactorybuild(Configuration config){returnnewDefaultSqlSessionFactory(config);}

SqlSessionFactory自动装配:引入SpringBoot自动装配Myabtis,在使用时一般仅需要配置DataSource数据源,mybatis-spring-boot-starter依赖引入了mybatis-spring-boot-autoconfigure依赖,META-INF目录的spring.factories(springboot自动配置文件)加载MybatisAutoConfiguration

//基于SqlSessionFactory,SqlSessionFactoryBean@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})@ConditionalOnBean({DataSource.class})//mybatis.xx的配置项解析为一个Configuration封装在MybatisProperties中@EnableConfigurationProperties({MybatisProperties.class})//数据源配置完成后才进行mybatis配置,也就是自动装配的spring.datasource配置@AutoConfigureAfter({DataSourceAutoConfiguration.class})publicclassMybatisAutoConfiguration{//这个config会注册一个注入了DataSource的SqlSessionFactory@Bean@ConditionalOnMissingBeanpublic SqlSessionFactorysqlSessionFactory(DataSource dataSource){}}//不从ioc缓存拿,每次获取最新的配置项@Configuration(proxyBeanMethods=false)@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class})//spring.datasource.xx配置项@EnableConfigurationProperties(DataSourceProperties.class)//其他依赖bean@Import({xx})publicclassDataSourceAutoConfiguration{

SringBoot集成mybatis的配置项

#datasource配置spring:datasource:username: rootpassword:1234url: jdbc:mysql://localhost:3306/mytestdriver-class-name: com.mysql.jdbc.Driver、#mybatis配置mybatis:mapper-locations: classpath:mapper/*Mapper.xmltype-aliases-package: com.mytest.entity

SqlSessionManager:SqlSessionFactory有两个实现类,DefaultSqlSessionFactory和SqlSessionManager,从结构上来看,SqlSessionManager对DefaultSqlSessionFactory进行了封装,并同时实现了SqlSession接口

SqlSessionManager sqlSessionManager= SqlSessionManager.newInstance(sqlSessionFactory);publicstatic SqlSessionManagernewInstance(SqlSessionFactory sqlSessionFactory){returnnewSqlSessionManager(sqlSessionFactory);}privateSqlSessionManager(SqlSessionFactory sqlSessionFactory){this.sqlSessionFactory= sqlSessionFactory;//private final SqlSession sqlSessionProxy;//创建sqlSession的代理this.sqlSessionProxy=(SqlSession)Proxy.newProxyInstance(classLoader,newClass[]{SqlSession.class},newSqlSessionManager.SqlSessionInterceptor());}//SqlSessionManager提供了一个本地线程变量privatefinal ThreadLocal<SqlSession> localSqlSession=newThreadLocal();publicvoidstartManagedSession(){this.localSqlSession.set(this.openSession());}

generator插件

<!--mybatis的generator插件-->
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.2</version>
    <configuration>
        <verbose>true</verbose>
        <!-- 代表mybatis generator生成的内容不要覆盖已有的内容 -->
        <overwrite>false</overwrite>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.21</version>
        </dependency>
    </dependencies>
</plugin>

多数据源Datasource配置:本质上就是配置两套sqlSessionFactory

首先是多数据源的连接信息

# master 数据源配置
master.datasource.url=jdbc:mysql://localhost:3306/db_1
master.datasource.username=root
master.datasource.password=321
master.datasource.driverClassName=com.mysql.jdbc.Driver

# second 数据源配置
second.datasource.url=jdbc:mysql://localhost:3306/db2
second.datasource.username=root
second.datasource.password=321
second.datasource.driverClassName=com.mysql.jdbc.Driver

手动构建sqlSessionFactory

@Configuration// 扫描 Mapper 接口并容器管理@MapperScan(basePackages= MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef="masterSqlSessionFactory")publicclassMasterDataSourceConfig{// 精确到 master 目录,以便跟其他数据源隔离staticfinal String PACKAGE="org.spring.springboot.mapper.master";staticfinal String MAPPER_LOCATION="classpath:mapper/master/*.xml";//数据源连接要素@Value("${master.datasource.url}")private String url;..//注册一个数据源@Bean(name="masterDataSource")@Primarypublic DataSourcemasterDataSource(){
        DruidDataSource dataSource=newDruidDataSource();
        dataSource.setDriverClassName(driverClass);...return dataSource;}//Spring提供的事务管理,对数据源作aop,所以需要注册不同切面体的bean@Bean(name="masterTransactionManager")@Primarypublic DataSourceTransactionManagermasterTransactionManager(){returnnewDataSourceTransactionManager(masterDataSource());}@Bean(name="masterSqlSessionFactory")@Primarypublic SqlSessionFactorymasterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)final SqlSessionFactoryBean sessionFactory=newSqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(newPathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));return sessionFactory.getObject();}}

SqlSession/Executor

sql会话实例用于和数据库交互,执行命令,映射器和管理事务,通过openSessionFromConnection构建SqlSession;简单理解sqlSession就和jdbc的statement一样可以去请求db处理sql了,直接通过api交由executor去执行或者创建mapper代理去执行

SqlSession sqlSession= sqlSessionFactory.openSession();//默认的DefaultSqlSessionpublicDefaultSqlSession(Configuration configuration, Executor executor,boolean autoCommit){this.configuration= configuration;this.executor= executor;//标记是否为更新sql,意思是,当update等操作数据库时需要进行事务操作this.dirty=false;this.autoCommit= autoCommit;}//主要参数,sql执行器,事务的隔离级别,是否自动提交事务private SqlSessionopenSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,boolean autoCommit){
    Environment environment= configuration.getEnvironment();//选择environment的transactionManager//默认是ManagedTransactionFactory创建的ManagedTransaction事务管理器
    TransactionFactory transactionFactory=getTransactionFactoryFromEnvironment(environment);//构建事务实例
    Transaction tx= transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//构建一个sql执行器并封装事物,BatchExecutor/ReuseExecutor/SimpleExecutor//本质上sql操作都是由Executor处理的,所以需要封
  • 作者:vanchine
  • 原文链接:https://blog.csdn.net/vanchine/article/details/112432654
    更新时间:2022-08-18 10:58:56