精华内容
下载资源
问答
  • Android,我么用原生还是混合?

    千次阅读 2017-09-03 00:07:12
    对于原生态开发、web 应用开发以及混合模式开发,本人认为并不是哪一种就是最好的,哪一种就是最差的,这个完全是根据项目的实际需求,选择一种合适的开发模式。他们同时具备自己的优点,同时也有自身的缺点,我们...

    本文将从4个角度,讲解分析混合开发,与使用方式,希望给大家更多思路。

                           前言

            最近开发几个项目,牵涉到Android的几种开发模式。对于原生态开发、web 应用开发以及混合模式开发,本人认为并不是哪一种就是最好的,哪一种就是最差的,这个完全是根据项目的实际需求,选择一种合适的开发模式。他们同时具备自己的优点,同时也有自身的缺点,我们根据实际情况,取其中的优点,尽量避免掉缺点,才是最好的开发模式。下面,我们就一同看看,这三种开发模式,到底有什么区别。下面结合现有网上资料,感觉还是很不错,和大家分享一下。


    一、原生应用 (也称本地开发 Native App 

    你使用过微软PowerPoint 或者 Word吧?这些可直接在你电脑上运行或者在智能手机上运行,简单来说,原生应用是特别为某种操作系统开发的,比如iOSandroid、黑莓等等,它们是在各自的移动设备上运行的。 

    优点: 

    • 可访问手机所有功能(GPS、摄像头);
    • 速度更快、性能高、整体用户体验不错;
    • 可线下使用(因为是在跟Web相对地平台上使用的);
    • 支持大量图形和动画; 容易发现(在App Store里面)和重新发现(应用图标会一直在主页上);
    • 应用下载能创造盈利(当然App Store抽取20-30% 的营收)。
    缺点: 

    • 开发成本高;
    • 支持设备非常有限(一般是哪个系统就在哪个平台专属设备上用);
    • 上线时间不确定(App Store审核过程不一);
    • 内容限制(App Store限制);
    • 获得新版本时需重新下载应用更新。
    举例:Yellow Pages Group 就开发了是几个版本(iOS、黑莓、Android),他们在每一个原生应用市场都有上架。 

    二、Web 应用App(PHPforAndroid等) 

    Web应用本质上是为移动浏览器设计的基于Web的应用,它们是用普通Web开发语言开发的,可以在各种智能手机浏览器上运行。 

    优点: 

    • 支持设备广泛;
    • 较低的开发成本;
    • 可即时上线;
    • 无内容限制;
    • 用户可以直接使用最新版本(自动更新,不需用户手动更新)。
    缺点: 

    • 表现略差(对联网的要求比较大);
    • 用户体验没那么炫;
    • 图片和动画支持性不高;
    • 没法在App Store中下载、无法通过应用下载获得盈利机会;
    • 要求联网;
    • 对手机特点有限制(摄像头、GPS等)。
    对于这些缺点,如果能把HTML 5的优点用到Web上的话就会得到很大改善,尽管技术在提高, 目前它还不能做原生应用可以做的每件事。 

    有些公司,比如金融时报继原生应用后也开发了Web应用,用户通过浏览器来访问他们的应用,因为他们的应用要采取应用内购买,而App Store是不允许应用内购买的,不然它们就无法拿到那20%-30% 的抽成了。 

    三、混合应用(HyBrid App 开发,Java+H5)

    混合应用大家都知道是原生应用和Web应用的结合体,采用了原生应用的一部分、Web应用的一部分,所以必须在部分在设备上运行、部分在Web上运行。 

    不过混合应用中比例很自由,比如Web 占90%,原生占10%;或者各占50%;再或者原生占80%,Web占20%。 

    优点: 

    • 兼容多平台;
    • 顺利访问手机的多种功能;
    • App Store中可下载(Wen应用套用原生应用的外壳);
    • 可线下使用。
    缺点: 

    • 不确定上线时间;
    • 用户体验不如本地应用;
    • 性能稍慢(需要连接网络);
    • 技术还不是很成熟。

    比如Facebook现在的应用属于混合应用它可以在许多App Store畅通无阻,但是掺杂了大量Web特性,所以它运行速度比较慢,而现在为了提高性能FB又决定采用原生应用。 



    四、工具和框架

    混合开发一些开发工具和框架其实很早就有了,比如AppCan、PhoneGap(Cordova)等,他们都主张一站式开发,简单的说,一个纯html5的开发人员就可以开发和发布Android和iOS的App了,不需要专门的Android和iOS开发人员。虽然感觉这样开发起来很快很简单,但是遇到问题和特殊需求的时候,就开始头疼了,再加上框架本身也存在一定的性能消耗,在Android 5.0和iOS 9.0以下,开发出来的App就显得很鸡肋。所以依我的经验来看,并不推荐找几个纯html5的开发人员就开始“干大事”。

    1、努力做到原生部分占20%,html5部分占80%。

    2、原生将实现:App主界面框架(可能含启动页面)、要求高性能的页面(如聊天/视频通话页面)、复杂算法/重要信息的存储或通信。

    而目前这两年来说(2017~2018年),大部分App只能做到35%左右的原生以及65%左右的html5。

    一个混合App项目的前端开发人员的分布大概会是1:1:2.5,即:

    小型项目:1个Android,1个iOS,2~3个html5

    中大型项目:2个Android,2个iOS,4~6个html5

    一般来讲,也是我目前知道的两种主流的方式就是

    1. js调用Native中的代码
    2. Schema:WebView拦截页面跳转

    第2种方式实现起来很简单,但是一个致命的问题就是这种交互方式是单向的,Html 5无法实现回调。像云音乐App中这种点击跳转到具体页面的功能,Schema的方式确实可以简单实现,而且也非常适合。如果需求变得复杂,假如Html 5需要获取Native App中的用户信息,那么最好使用js调用的方式。


    一、js和Native进行交互

    上面讲到WebViewbe本身就是支持js调用Native代码的,不过WebView的这个功能在Android 4.2(API 17)一下存在高危的漏洞。这个漏洞的原理就是Android系统通过 WebView.addJavascriptInterface(Object o, String interface) 方法注册可供js调用的Java对象,但是系统并没有对注册的Java对象方法调用做限制。导致攻击者可以利用反射调用未注册的其他任何Java对象,攻击者可以根据客户端的能力做任何事情。 这篇文章 详细的介绍了这个漏洞

    出于安全考虑,Android 4.2以后的系统规定允许被js调用的Java方法必须以 @JavascriptInterface 进行注解

    二、Cordova的解决方案

    Cordova是一个广泛使用的Hybrid开发框架,它提供了一套js和Native交互规范

    在Cordova的 SystemWebViewEngine 类中可以看到


    三、小众工具

    1)Calabash(适用于Android和iOS)

    Calabash是一个开源的验收测试框架,支持Android和iOS自动化。Calabash为Android和iOS自动化测试提供了一个单独的库。这是一个跨平台的框架,支持Cucumber,Cucumber能让你用自然的英语语言表述app的行为,实现BDD(Behavior Driven Development,行为驱动开发)。

    Calabash提供了一个桥梁,允许Cucumber测试对iOS和Android运行和验证。Cucumber测试使用一列语句写入,这些语句会形成很多测试场景。Cucumber中的所有语句使用Ruby定义。

    在Calabash,Cucumber语句只能被定义一次,但可以在Cucumber脚本的不同场景中重复使用。

    实际测试是用Gherkin写的,依靠Ruby代码的支持,并在Cucumber框架的上下文中运行。
    2)Appium(Android和iOS)

    Appium是Sauce Labs出品的一个开源的自动化测试框架,用于原生、混合和移动的web app。框架内的Appium库函数调用Appium服务器是在操作连接设备的后台运行的。它在内部使用JSONWireProtocol,来与使用Selenium的WebDriver的iOS和Android app进行互动。

    不像Calabash只支持Ruby开发,在框架中使用Appium时,你可以从Java、Python和Ruby,以及所有其他Selenium WebDriver支持的语言中选择。

    Appium服务器被托管在Node服务器上。你可以通过触发一组Node命令来启动Appium服务器。使用Appium Standalone Application作为服务器(从Appium网站下载),Inspector工具可对app的所有定位器提供查找/识别/操作的能力。


    3)Robotium(Android )

    Robotium是一个开源的测试框架,用于开发功能性,系统,和验收测试场景。它与Selenium非常相似,除了Robotium只适用于Android。它注册在Apache License 2.0下。

    因为它不但简单,而且又具有创建强大又可靠的自动化场景的能力,因而它在自动化测试社区广泛流行。

    它采用运行时绑定到GUI组件。它安装了一个测试用例套件作为在Android设备或仿真器上的应用程序,并提供用于执行测试的真实环境。

    优点

    容易在最短的时间内编写测试脚本。

    预装自动化app是可能的。

    自动跟随当前activity。

    由于运行时绑定到GUI组件,所以相比Appium,它的测试执行更快,更强大。

    不访问代码或不知道app实现,也可以工作。

    支持Activities、Dialogs、Toasts、Menus、Context Menus和其他Android SDK控件。

    缺点

    不能处理flash和web组件。

    支持Java开发。

    在旧设备上会变得很慢。

    由于不支持iOS设备,当自动化测试同时覆盖 android与iOS的情况时,测试会被中断。

    没有内置的记录和回放功能. 使用记录功能需要 TestDroid 和 Robotium Recorder 这样收费工具。

    4)Frank(iOS)

    Frank是一个iOS app的自动化框架,允许使用Cucumber编写结构化英语句子的测试场景。

    Frank要求测试时在应用程序内部编译,这意味着对源代码的改变是强制性的。这是一个使用Cucumber和JSON组合命令的工具,命令发送到在本地应用程序内部运行的服务器上,并利用UISpec运行命令。

    优点

    测试场景是在Cucumber的帮助下,用可理解的英语句子写的。

    Symbiote——包含实时检查工具。

    如果团队有关于web selenium和cucumber自动化框架的经验,也有效。


    5)UIAutomator(Android)

    UIAutomator是由谷歌提供的测试框架,它提供了原生Android app和游戏的高级UI测试。这是一个包含API的Java库,用来创建功能性UI测试,还有运行测试的执行引擎。该库自带Android SDK。有很多教程可供初学者上手。优点是它在运行访问不同的进程时,会给JUnit测试案例特权。虽然这对本地自动化app既好又更简单,但是它对web自动化视图非常有限或几乎没有任何支持。它仅支持使用API level 16及以上的设备,不过这也不算什么很大的因素,因为现在大多数的app支持API level 19及以上。

    优点

    简单易学的教程。

    库由谷歌社区支持和维护。

    第三方支付集成了基于云计算的测试管理。

    缺点

    仅支持android 4.1及以上。


    展开全文
  • mybatis简介  mybatis是一个Java持久层框架,...1、使用jdbc程序使用原生态的jdbc进行开发存在很多弊端,优点是执行效率高,mybatis弥补了jdbc的缺陷。 2、mybatis的架构(重点)。 3、mybatis的入门程序(重点)。  实

    mybatis简介

     mybatis是一个Java持久层框架,Java中操作关系型数据库使用的是jdbc,mybatis是对jdbc的封装。

    mybatis的入门需要掌握以下几点:

    1、使用jdbc程序使用原生态的jdbc进行开发存在很多弊端,优点是执行效率高,mybatis弥补了jdbc的缺陷。

    2、mybatis的架构(重点)。

    3、mybatis的入门程序(重点)。

         实现数据的查询、添加、修改、删除

    4、mybatis开发DAO的两种方法(重点)

         原始的DAO开发方式(DAO接口和DAO实现都需要编写)

         mapper代理方式(只需要编写DAO接口)

    5、输入映射类型和输出映射类型

    6、动态SQL

    mybatis的高级知识主要包括以下几点:

    高级映射查询(一对一、一对多、多对多)(重点)

    查询缓存

    延迟加载

    mybatis和Spring整合(重点)

    mybatis逆向工程

    开发环境(eclipse、MySQL)

    创建数据库

    创建数据库mybatis

    新建表结构:

    sql_table.sql

    *
    SQLyog v10.2 
    MySQL - 5.1.72-community : Database - mybatis
    *********************************************************************
    */
    
    
    /*!40101 SET NAMES utf8 */;
    
    /*!40101 SET SQL_MODE=''*/;
    
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    /*Table structure for table `items` */
    
    CREATE TABLE `items` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(32) NOT NULL COMMENT '商品名称',
      `price` float(10,1) NOT NULL COMMENT '商品定价',
      `detail` text COMMENT '商品描述',
      `pic` varchar(64) DEFAULT NULL COMMENT '商品图片',
      `createtime` datetime NOT NULL COMMENT '生产日期',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    /*Table structure for table `orderdetail` */
    
    CREATE TABLE `orderdetail` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `orders_id` int(11) NOT NULL COMMENT '订单id',
      `items_id` int(11) NOT NULL COMMENT '商品id',
      `items_num` int(11) DEFAULT NULL COMMENT '商品购买数量',
      PRIMARY KEY (`id`),
      KEY `FK_orderdetail_1` (`orders_id`),
      KEY `FK_orderdetail_2` (`items_id`),
      CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
      CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
    
    /*Table structure for table `orders` */
    
    CREATE TABLE `orders` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `user_id` int(11) NOT NULL COMMENT '下单用户id',
      `number` varchar(32) NOT NULL COMMENT '订单号',
      `createtime` datetime NOT NULL COMMENT '创建订单时间',
      `note` varchar(100) DEFAULT NULL COMMENT '备注',
      PRIMARY KEY (`id`),
      KEY `FK_orders_1` (`user_id`),
      CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
    
    /*Table structure for table `user` */
    
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(32) NOT NULL COMMENT '用户名称',
      `birthday` date DEFAULT NULL COMMENT '生日',
      `sex` char(1) DEFAULT NULL COMMENT '性别',
      `address` varchar(256) DEFAULT NULL COMMENT '地址',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    
    创建数据

    sql_data.sql

    /*
    SQLyog v10.2 
    MySQL - 5.1.72-community : Database - mybatis
    *********************************************************************
    */
    
    
    /*!40101 SET NAMES utf8 */;
    
    /*!40101 SET SQL_MODE=''*/;
    
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    /*Data for the table `items` */
    
    insert  into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'台式机',3000.0,'该电脑质量非常好!!!!',NULL,'2015-02-03 13:22:53'),(2,'笔记本',6000.0,'笔记本性能好,质量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'背包',200.0,'名牌背包,容量大质量好!!!!',NULL,'2015-02-06 13:23:02');
    
    /*Data for the table `orderdetail` */
    
    insert  into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3);
    
    /*Data for the table `orders` */
    
    insert  into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL);
    
    /*Data for the table `user` */
    
    insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'张三','2014-07-10','1','北京市'),(16,'张小明',NULL,'1','河南郑州'),(22,'陈小明',NULL,'1','河南郑州'),(24,'张三丰',NULL,'1','河南郑州'),(25,'陈小明',NULL,'1','河南郑州'),(26,'王五',NULL,NULL,NULL);
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    

    jdbc编程中的问题

     企业开发中,根据项目大小、特点进行技术选型,jdbc操作数据库时效率时很高的,jdbc也是结束选型的参考

    jdbc程序 

     需要数据库驱动包


    上边是MySQL的驱动,下边是oracle的驱动

    参考下边一段程序

    package test.lx.mybatis.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
     * jdbc的测试程序
     * 
     * @author lx
     * 
     */
    public class JdbcTest {
    	public static void main(String[] args) {
    		Connection connection = null;
    		//PreparedStatement是预编译的Statement,通过Statement发起数据库的操作
    		//PreparedStatement防止sql注入,执行数据库效率高
    		PreparedStatement preparedStatement = null;
    		ResultSet resultSet = null;
    		
    		try {
    			//加载数据库驱动
    			Class.forName("com.mysql.jdbc.Driver");
    			
    			//通过驱动管理类获取数据库链接
    			connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
    			//定义sql语句 ?表示占位符
    		String sql = "select * from user where username = ?" ;
    			//获取预处理statement
    			preparedStatement = connection.prepareStatement(sql);
    			//设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
    			preparedStatement.setString(1, "王五");
    			//向数据库发出sql执行查询,查询出结果集
    			resultSet =  preparedStatement.executeQuery();
    			//遍历查询结果集
    			while(resultSet.next()){
    				System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			//释放资源
    			if(resultSet!=null){
    				try {
    					resultSet.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if(preparedStatement!=null){
    				try {
    					preparedStatement.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if(connection!=null){
    				try {
    					connection.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    
    		}
    
    	}
    }

    jdbc问题总结

    1、数据库连接频繁的创建和关闭,缺点浪费数据库的资源,影响操作效率
          设想:使用数据库连接池
    2、SQL语句是硬编码,如果需求变更需要修改SQL,就需要修改Java代码,需要重新编译,系统不易维护。
          设想:将SQL语句统一配置在文件中,修改SQL不需要修改Java代码
    3、通过PreparedStatement向占位符设置参数,存在硬编码(参数位置,参数)问题。系统不易维护。
          设想:将SQL中的占位符以及对应的参数类型配置在配置文件中,能够自动输入映射
    4、遍历查询结果集中存在硬编码(列名)
          设想:自动进行SQL查询结果向Java对象的映射(输出映射)

    mybatis架构(重点)

    mybatis介绍:
    • mybatis本是Apache的一个开源项目ibatis,2010年这个项目由Apache software foundation迁移到了Google code,并且改名为mybatis,实质上mybatis对ibatis进行了一些改进。目前mybatis在GitHub上托管。git(分布式版本控制,当前比较流行)
    • mybatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
    • mybatis通过xml或注解的方式将要执行的各种statement( statement、PreparedStatement、CallableStatement)配置起来,并通过Java对象和Statement中的SQL进行映射生成最终执行的SQL语句,最后由mybatis框架执行SQL并将结果映射成Java对象并返回。
    mybatis架构

    mybatis入门程序

    需求

    实现用户查询:

    根据用户的id查询用户的信息(单条记录)

    根据用户名模糊查询用户信息(多条记录)

    用户添加、用户修改、用删除

    导入jar包

    从mybatis官网下载地址是: https://github.com/mybatis/mybatis-3/releases


    mybatis-3.2.7.pdf —操作手册

    mybatis-3.2.7.jar— 核心jar

    lib—依赖jar包


    工程结构


    log4j.properties(公用文件)

    建议开发环境中要使用debug

    # Global logging configuration\uff0c\u5efa\u8bae\u5f00\u53d1\u73af\u5883\u4e2d\u8981\u7528debug
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    

    SqlMapConfig.xml(公用文件)

    通过SqlMapConfig.xml加载mybatis运行环境
    <?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>
    	<!-- 和Spring整合后environments配置将废除 -->
    	<environments default="development">
    		<environment id="development">
    			<!-- 使用jdbc事务管理 -->
    			<transactionManager type="JDBC" />
    			<dataSource type="POOLED">
    				<property name="driver" value="com.mysql.jdbc.Driver" />
    				<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
    				<property name="username" value="root" />
    				<property name="password" value="root" />
    			</dataSource>
    		</environment>
    	</environments>
    
    	<!-- 加載mapper文件 -->
    	<mappers>
    		<mapper resource="sqlmap/User.xml" />
    	</mappers>
    </configuration>
    

    根据id查询用户

    pojo (User.java)

    public class User {
    	private int id;
    	private String username; // 用户姓名
    	private String sex; // 性别
    	private Date birthday; // 生日
    	private String address; // 地址
            // 添加对应的setter和getter方法
            ......
    }

    User.xml (重点)

    建议命名规则:表名+mapper.xml
    早期ibatis命名规则:表名.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命名空间,为了对SQL语句进行隔离,方便管理,mapper可开发dao方式,使用namespace有特殊作用 
    mapper代理开发时将namespace指定为mapper接口的全限定名 -->
    <mapper namespace="test">
    <!-- 在mapper.xml文件中配置很多的SQL语句,执行每个SQL语句时,封装为MappedStatement对象
    mapper.xml以statement为单位管理SQL语句
     -->
     
     <!-- 根据id查询用户信息 -->
     <!-- 
        id: 唯一标识一个statement
        #{}:表示一个占位符,如果#{} 中传入简单类型的参数,#{}中的名称随意
        parameterType: 输入参数的类型,通过#{}接收parameterType输入的参数
        resultType:输出结果类型,指定单条记录映射的pojo类型
      -->
      <select id="findUserById" parameterType="int" resultType="test.lx.mybatis.po.User">
         SELECT * FROM USER WHERE id=#{id};
      </select>
      
    </mapper>

    编码

    创建SqlSessionFactory
                    // 会话工厂
    		private SqlSessionFactory sqlSessionFactory;
    		// 创建工厂
    		@Before
    		public void init() throws IOException {
    
    			// 配置文件(SqlMapConfig.xml)
    			String resource = "SqlMapConfig.xml";
    
    			// 加载配置文件到输入 流
    			InputStream inputStream = Resources.getResourceAsStream(resource);
    
    			// 创建会话工厂
    			sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
    		}
    
    		// 测试根据id查询用户(得到单条记录)
    		@Test
    		public void testFindUserById() {
    
    			// 通过sqlSessionFactory创建sqlSession
    
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    
    			// 通过sqlSession操作数据库
    			// 第一个参数:statement的位置,等于namespace+statement的id
    			// 第二个参数:传入的参数
    			User user = null;
    			try {
    				user = sqlSession.selectOne("test.findUserById", 1);
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    
    			System.out.println(user);
    
    		}

    根据用户名称模糊查询用户信息

    根据用户名称模糊查询用户信息可能返回多条记录。

    User.xml

     <!-- 根据用户名称查询用户信息,可能返回多条 
      ${}:表示SQL的拼接,通过${}接收参数,将参数的内容不加任何修饰的拼接在SQL中
      -->
      <select id="findUserByName" parameterType="java.lang.String" resultType="test.lx.mybatis.po.User">
           select * from user where username like '%${value}%' 
      </select>
      
      <select id="findUserByName2" parameterType="java.lang.String" resultType="test.lx.mybatis.po.User">
           select * from user where username like #{username} 
      </select>

    编码

    // 测试根据名称模糊查询用户(可能得到多条记录)
    		@Test
    		public void testFindUserByName() {
    			
    			// 通过sqlSessionFactory创建sqlSession
    			
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			// 通过sqlSession操作数据库
    			// 第一个参数:statement的位置,等于namespace+statement的id
    			// 第二个参数:传入的参数
    			List<User> list = null;
    			try {
    				//list = sqlSession.selectList("test.findUserByName", "小明");
    				list = sqlSession.selectList("test.findUserByName2", "%小明%");
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    			
    			System.out.println(list.size());
    		}

    mybatis开发过程小结

    1.编写SqlMapConfig.xml
    2.编写mapper.xml(定义了statement)
    3.编程通过配置文件创建SqlSessionFactory
    4.通过SqlSessionFactory获取SqlSession
    5.通过SqlSession操作数据库(如果执行修改、添加、删除需要调用SqlSession.commit())
    6.SqlSession使用完后后要关闭

    用户添加

    向用户表中添加一条数据记录

    User.xml

     <!-- 添加用户 
       parameterType:输入参数的类型,User对象包括username,birthday,sex,address
       #{}接收pojo数据,可以使用OGNL解析出pojo的属性值
       #{username}表示从parameterType中获取pojo的属性值
      -->
      <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert>

    编码

    		@Test
    		public void testInsertUser() {
    			// 通过sqlSessionFactory创建sqlSession
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			//通过sqlSession操作数据库
    			//创建插入数据对象
    			User user = new User();
    			user.setUsername("一蓑烟雨");
    			user.setAddress("河南周口");
    			user.setBirthday(new Date());
    			user.setSex("1");
    			try {
    				sqlSession.insert("test.insertUser", user);
    				//需要提交事务
    				sqlSession.commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    			
    		}


    测试结果如下


    主键返回

    需求:user对象插入到数据库后,新纪录的主键要通过user对象返回,通过user获取主键值。
    解决思路:
    通过LAST_INSERT_ID()获取刚插入记录的自增主键值,在insert语句执行后,执行select LAST_INSERT_ID()就可以获取自增主键。注意:此语句必须和INSERT语句一块使用并且要在插入后执行。
    修改User.xml
    <!-- 添加用户 
       parameterType:输入参数的类型,User对象包括username,birthday,sex,address
       #{}接收pojo数据,可以使用OGNL解析出pojo的属性值
       #{username}表示从parameterType中获取pojo的属性值
       <selectKey>:用于进行主键返回,定义了主键值的SQL
       order:设置selectKey标签中SQL的执行顺序,相对于insert语句而言
       keyProperty: 将主键设置到哪个属性上
       resultType:select LAST_INSERT_ID()的结果类型
      -->
      <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
         <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID()
         </selectKey>
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert>
    使用MySQL的uuid机制生成主键:
    使用uuid生成主键的好处是不考虑数据库移植后的主键冲突问题
    实现思路:先查询uuid得到主键,将主键设置到user对象中,将user插入到数据库中

     <!-- mysql的uuid()函数生成主键 -->
      <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
         <selectKey keyProperty="id" order="BEFORE" resultType="string">
            select uuid()
         </selectKey>
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert>
    如何实现oracle数据库的主键返回?
    oracle没有自增主键机制,使用序列完成主键生成

    实现思路:先查询序列得到主键,将主键设置到user对象中,将user对象插入数据库

    <!-- oracle
             在执行insert之前执行select 序列.nextval() from dual取出序列最大值,将值设置到user对象的id属性中
       -->
      <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
         <selectKey keyProperty="id" order="BEFORE" resultType="int">
            select 序列.nextval() from dual
         </selectKey>
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert>

    用户删除和更新

    User.xml

     <!-- 用户删除 -->
      <delete id="deleteUser" parameterType="int">
       delete from user where id=#{id}
      </delete>
      <!-- 用户更新
      	要求:传入的user对象包括id属性值
       -->
       <update id="updateUser" parameterType="test.lx.mybatis.po.User">
       update user set username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
       </update>

    编码

    // 测试删除用户
    		@Test
    		public void testDeleteUser() {
    			// 通过sqlSessionFactory创建sqlSession
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			//通过sqlSession操作数据库
    			try {
    				sqlSession.delete("test.deleteUser", 28);
    				//需要提交事务
    				sqlSession.commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    		}
    		// 测试根据id更新用户(得到单条记录)
    		@Test
    		public void testUpdateUser() {
    			// 通过sqlSessionFactory创建sqlSession
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			//通过sqlSession操作数据库
    			//创建更新数据库对象,要求必须包括id
    			User user= new User();
    			user.setId(28);
    			user.setUsername("任平生");
    			//凡是没有设置的属性都被当成了NULL进行赋值
    			//user.setBirthday(new Date());
    			user.setSex("1");
    			
    			try {
    				sqlSession.delete("test.updateUser", user);
    				//需要提交事务
    				sqlSession.commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    		}

    Mybatis解决jdbc编程中的问题

    1.数据库链接创建、释放频繁造成系统资源浪费从而影响性能,如果使用数据库连接池可以解决此问题。
       解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库连接。
    2.SQL语句写在代码中造成代码不易维护,实际应用SQL变化可能较大,SQL变动需要改变Java代码。
       解决:将SQL语句配置在XXXXMapper.xml文件中与Java代码分离。
    3.向SQL语句中传参数麻烦,因为SQL语句的where条件不一定,可能多也可能少,占位符和参数要一一对应。
       解决:Mybatis自动将Java对象映射至SQL语句,通过statement中的parameterType定义输入参数的类型。
    4.对结果集解析麻烦,SQL变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
       解决:Mybatis自动将SQL执行结果映射至Java对象,通过statement中的resultType定义输出结果的类型

    Mybatis与Hibernate的重要区别

    企业开发进行选型,考虑mybatis和Hibernate适用场景。

    Mybatis:入门简单,程序容易上手开发,节省开发成本。Mybatis需要程序员自己编写SQL语句,是一个不完全的ORM框架,对SQL修改和优化非常容易实现。Mybatis适合开发需求变更频繁的系统,比如:互联网项目。

    Hibernate:入门门槛高,如果使用Hibernate写出高性能的程序不容易实现。Hibernate不用写SQL语句,是一个完全的ORM框架。Hibernate适合需求固定,对象数据模型稳定,中小型项目,比如:企业OA系统。

    总之,企业在技术选型时根据项目实际情况,以降低成本和提高系统可维护性为出发点进行技术选型。

    总结

    SqlMapConfig.xml

    是mybatis全局配置文件,只有一个,名称不固定,主要mapper.xml,mapper.xml中配置SQL语句。

    mapper.xml

    mapper.xml是以statement为单位进行配置。(把一个SQL称为一个statement),statement中配置SQL语句、parameterType输入参数类型(完成输入映射)、resultType输出结果类型(完成输出映射)。

    还提供了parameterMap配置输入参数类型(已过期,不推荐使用)

    还提供了resultMap配置输出结果类型(完成输出映射)。

    占位符#{}

    #{}表示一个占位符吗,向占位符输入参数,mybatis自动进行Java类型和jdbc类型的转换。程序员不需要考虑参数的类型,比如传入字符串,mybatis最终拼接好的SQL就是参数两边加上单引号。#{} 接收pojo数据,可以使用OGNL解析出pojo的属性值。

    拼接符${}

    表示SQL的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在SQL中。${}也可以接收pojo的数据,可以使用OGNL解析出pojo的属性值。

    缺点:不能防止SQL注入。

    selectOne和selectList

    selectOne用于查询单条记录,不能用于查询多条记录,否则会抛出异常。而selectList用户查询多条记录,也可用于查询单条记录。

    mybatis开发DAO的方法

    SqlSession的作用范围

    SqlSessionFactoryBuilder: 是以工具类的方式来使用,需要创建SqlSessionFactory时就new一个SqlSessionFactoryBuilder。
    SqlSessionFactory:正常开发时,以单例方式管理SqlSessionFactory,整个系统运行过程中SqlSessionFactory只有一个实例,将来和Spring整合后由Spring以单例模式管理SqlSessionFactory。
    SqlSession: SqlSession是一个面向用户(程序员)的接口,程序员调用SqlSession的 接口方法进行操作数据库
    问题:SqlSession是否能以单例方式使用?
    由于SqlSession是线程不安全的,所以SqlSession最佳应用范围是在方法体内,在方法体内定义局部变量使用SqlSession。

    原始DAO的开发方式

    程序员需要编写DAO接口和DAO的实现类

    DAO接口

    public interface UserDao {
    	// 根据id查询用户信息
    	public User findUserById(int id) throws Exception;
    }

    DAO接口的实现

    public class UserDaoImpl implements UserDao {
       private SqlSessionFactory sqlSessionFactory;
       
       //将SqlSessionFactory注入
       public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
    	   this.sqlSessionFactory = sqlSessionFactory;
       }
    	
    	public User findUserById(int id) throws Exception {
    		//创建SqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		
    		//根据id查询用户信息
    		User user = sqlSession.selectOne("test.findUserById", id);
    		sqlSession.close();
    		return user;
    	}
    }

    测试代码

    public class UserDaoImplTest {
    	// 会话工厂
    	private SqlSessionFactory sqlSessionFactory;
    	
    	//创建工厂
    	@Before
    	public void init() throws IOException{
    		//配置文件(SqlMapConfig.xml)
    		String resource = "SqlMapConfig.xml";
    		
    		//加载配置文件到输入流
    		InputStream inputStream = Resources.getResourceAsStream(resource);
    		
    		//创建会话工厂
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);		
    	}
    	
    	@Test
    	public void testFindUserById() throws Exception{
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    		
    		User user = userDao.findUserById(1);
    		System.out.println(user);
    	}
    	
    }

    mapper代理方式

    对于mapper代理的方式,程序员只需要写DAO接口,DAO接口实现对象由mybatis自动生成代理对象。本身DAO在三层架构中就是一个通用的接口。

    原始DAO开发方式的问题

    • DAO的实现类中存在重复代码,整个mybatis操作的过程代码模板重复(先创建SqlSession、调用SqlSession的方法、关闭SqlSession)
    • DAO的实现类中存在硬编码,调用SqlSession方法时将statement的id硬编码。

    mapper开发规范

    要想让mybatis自动创建DAO接口实现类的代理对象,必须遵循一些规则:

    1.mapper.xml中namespace指定为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">
    <!-- namespace命名空间,为了对SQL语句进行隔离,方便管理,mapper可开发dao方式,使用namespace有特殊作用 
    mapper代理开发时将namespace指定为mapper接口的全限定名 -->
    <mapper namespace="test.lx.mybatis.mapper.UserMapper">
    ......
    此步骤目的:通过mapper.xml和mapper.java进行关联

    2.mapper.xml中statement的id就是mapper.java中的方法名。

    3.mapper.xml中statement的parameterType和mapper.java中方法输入参数类型一致。

    4.mapper.xml中statement的resultType和mapper.java中方法返回值一致。

    mapper.xml(映射文件)

    mapper映射文件的命名方式建议: 表名Mapper.xml

    namespace指定为mapper接口的全限定名

     <!-- 根据id查询用户信息 -->
     <!-- 
        id: 唯一标识一个statement
        #{}:表示一个占位符,如果#{} 中传入简单类型的参数,#{}中的名称随意
        parameterType: 输入参数的类型,通过#{}接收parameterType输入的参数
        resultType:输出结果类型,不管返回是多条还是单条,指定单条记录映射的pojo类型
      -->
      <select id="findUserById" parameterType="int" resultType="test.lx.mybatis.po.User">
         SELECT * FROM USER WHERE id=#{id};
      </select>

    mapper接口

    mybatis提出了mapper接口,相当于DAO接口
    mapper接口的命名方式建议: 表名Mapper
    public interface UserMapper {
      
    	//根据用户id查询用户信息
    	public User findUserById(int id) throws Exception;
    }

    将mapper.xml在SqlMapConfig.xml中加载

    <!-- 加載mapper文件 -->
    	<mappers>
    		<mapper resource="sqlmap/User.xml" />
    		<mapper resource="test/lx/mybatis/mapper/UserMapper.xml"/>
    	</mappers>

    mapper接口返回单个对象和集合对象

    不管查询记录是单条还是多条,在statement中resultType定义一致,都是单条记录映射的pojo类型。
    mapper接口方法返回值,如果是返回的单个对象,返回值类型是pojo类型,生成的代理对象内部通过selectOne获取记录,如果返回值类型是集合对象,生成的代理对象内部通过selectList获取记录。
    //根据用户id查询用户信息
    	public User findUserById(int id) throws Exception;
    	
    	//根据用户姓名查询用户信息
    	public List<User> findUserByName(String username) throws Exception;

    问题

    返回值问题

    如果方法调用的statement,返回的是多条记录,而mapper.java方法的返回值为pojo,此时代理对象通过selectOne调用,由于返回多条记录,所以报错:
    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

    输入参数的问题

    使用mapper代理的方式开发,mapper接口方法输入参数只有一个,可扩展性是否很差?

    扩展性没有任何问题,因为DAO层就是通用的,可以通过扩展pojo(定义pojo包装类型)将不同的参数(可以是pojo也可以是简单类型)传入进去。

    SqlMapConfig.xml

    SqlMapConfig.xml中配置的内容和顺序如下:
    properties(属性)
    settings(全局配置参数)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境集合属性对象)
           environment(环境子属性对象)
                   transactionManager(事务管理器)
                   dataSource(数据源)
    mappers(映射器)

    properties属性定义

    可以把一些通用的属性值配置在属性文件中,加载到mybatis运行环境内,比如:创建db.properties配置数据库连接参数。

     <!-- 属性定义
                 加载一个properties文件
                 在properties标签中配置属性值
         -->
         <properties resource="db.properties">
             <!-- <property name="" value=""/> -->
         </properties>
    	<!-- 和Spring整合后environments配置将废除 -->
    	<environments default="development">
    		<environment id="development">
    			<!-- 使用jdbc事务管理 -->
    			<transactionManager type="JDBC" />
    			<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>

    注意:Mybatis将按照下面的顺序加载属性:

    • 在properties元素体内定义的属性首先被读取。
    • 然后会读取properties元素中resource或url加载的属性,它会覆盖已经读取的同名属性。
    • 最后读取parameterType传递的属性,它会覆盖已经读取的同名属性。

    建议使用properties,不要在properties中定义属性,只引用定义的properties文件中的属性,并且properties中定义的key要有一些特殊的规则。

    settings全局参数配置

    mybatis运行时可以调整一些全局参数,根据使用需求进行参数配置。注意:需要小心配置,配置的参数会影响mybatis的执行。

    ibatis的全局配置参数中包括很多的性能参数(最大线程数,最大等待时间...),通过调整这些参数使ibatis达到高性能的运行,mybatis没有这些性能参数,由mybatis自行调节。

    mybatis中全局参数配置示例如下:

     <settings>
            <setting name="cacheEnabled" value="true"/>
         </settings>
    还有许多全局参数,根据需求进行配置,如下表所示:

    Setting(设置) Description(描述) Valid Values(验证值组)Default(默认值)

    • cacheEnabled 在全局范围内启用或禁用缓存配置任何映射器在此配置下。取值范围(true | false),默认值为TRUE
    • lazyLoadingEnabled 在全局范围内启用或禁用延迟加载。禁用时,所有协会将热加载。取值范围(true | false),默认值为TRUE
    • aggressiveLazyLoading 启用时,有延迟加载属性的对象将被完全加载后调用懒惰的任何属性。否则,每一个属性是按需加载。取值范围(true | false),默认值为TRUE
    • multipleResultSetsEnabled 允许或不允许从一个单独的语句(需要兼容的驱动程序)要返回多个结果集。取值范围(true | false)默认值为TRUE。
    • useColumnLabel 使用列标签,而不是列名。在这方面,不同的驱动有不同的行为。参考驱动文档或测试两种方法来决定你的驱动程序的行为如何。取值范围(true | false),默认值为TRUE。
    • useGeneratedKeys 允许JDBC支持生成的密钥。兼容的驱动程序是必需的。此设置强制生成的键被使用,如果设置为true,一些驱动会不兼容性,但仍然可以工作。取值范围(true | false),默认值为FALSE。
    • autoMappingBehavior 指定MyBatis的应如何自动映射列到字段/属性。NONE自动映射。 PARTIAL只会自动映射结果没有嵌套结果映射定义里面。 FULL会自动映射的结果映射任何复杂的(包含嵌套或其他)。取值范围(NONE, PARTIAL, FULL),默认值为PARTIAL。
    • defaultExecutorType 配置默认执行器。SIMPLE执行器确实没有什么特别的。 REUSE执行器重用准备好的语句。 BATCH执行器重用语句和批处理更新。取值范围(SIMPLE REUSE BATCH),默认值为SIMPLE。
    • defaultStatementTimeout 设置驱动程序等待一个数据库响应的秒数。任意整型不允许为空(Any positive integer  Not Set (null))。
    • safeRowBoundsEnabled 允许使用嵌套的语句RowBounds。取值范围(true | false),默认值为FALSE。
    • mapUnderscoreToCamelCase 从经典的数据库列名A_COLUMN启用自动映射到骆驼标识的经典的Java属性名aColumn。取值范围(true | false),默认值为FALSE。
    • localCacheScope MyBatis的使用本地缓存,以防止循环引用,并加快反复嵌套查询。默认情况下(SESSION)会话期间执行的所有查询缓存。如果localCacheScope=STATMENT本地会话将被用于语句的执行,只是没有将数据共享之间的两个不同的调用相同的SqlSession。取值范围(SESSION | STATEMENT),默认值为SESSION。
    • dbcTypeForNull  指定为空值时,没有特定的JDBC类型的参数的JDBC类型。有些驱动需要指定列的JDBC类型,但其他像NULL,VARCHAR或OTHER的工作与通用值。JdbcType enumeration. Most common are: NULL, VARCHAR and OTHEROTHER
    • lazyLoadTriggerMethods 指定触发延迟加载的对象的方法。A method name list separated by commas equals,clone,hashCode,toString
    • defaultScriptingLanguage 指定所使用的语言默认为动态SQL生成。A type alias or fully qualified class name. org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver
    • callSettersOnNulls 指定如果setter方法​​或地图的put方法时,将调用检索到的值是null。它是有用的,当你依靠Map.keySet()或null初始化。注意原语(如整型,布尔等)不会被设置为null。取值范围(true | false) FALSE
    • logPrefix 指定的前缀字串,MyBatis将会增加记录器的名称。Any String Not set
    • logImpl 指定MyBatis的日志实现使用。如果此设置是不存在的记录的实施将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGINGNot set
    • proxyFactory 指定代理工具,MyBatis将会使用创建懒加载能力的对象。CGLIB | JAVASSIST

    typeAliases别名(常用)

    mybatis提供的别名

    别名

    映射的类型

    _byte

    byte

    _long

    long

    _short

    short

    _int

    int

    _integer

    int

    _double

    double

    _float

    float

    _boolean

    boolean

    string

    String

    byte

    Byte

    long

    Long

    short

    Short

    int

    Integer

    integer

    Integer

    double

    Double

    float

    Float

    boolean

    Boolean

    date

    Date

    decimal

    BigDecimal

    bigdecimal

    BigDecimal

    自定义别名

    <!-- 定义别名 -->
         <typeAliases>
         <!-- 
    	     单个别名定义
    	     alias:别名, type:别名映射类型
    	     <typeAlias type="test.lx.mybatis.po.User" alias="user"/>
          -->
          <!-- 批量别名定义
    		 指定包路径,自动扫描包内的pojo,定义别名,别名默认为类名(首字母小写或大写)      		
           -->
           <package name="test.lx.mybatis.po"/>
         </typeAliases>

    使用别名

    在parameterType、resultType中使用别名:
    <!-- 根据id查询用户信息 -->
     <!-- 
        id: 唯一标识一个statement
        #{}:表示一个占位符,如果#{} 中传入简单类型的参数,#{}中的名称随意
        parameterType: 输入参数的类型,通过#{}接收parameterType输入的参数
        resultType:输出结果类型,不管返回是多条还是单条,指定单条记录映射的pojo类型
      -->
      <select id="findUserById" parameterType="int" resultType="user">
         SELECT * FROM USER WHERE id=#{id};
      </select>

    typeHandlers

    类型处理器将Java类型和jdbc类型进行映射。
    mybatis默认提供很多类型处理器,一般情况下足够使用。

    mappers

    <!-- 加載mapper映射
    	如果和Spring整合后,可以使用整合包中的mapper扫描器,到那时此处的mapper就不用配置了
    	-->
    	<mappers>
    	     <!-- 通过resource映入mapper的映射文件 -->
    		<mapper resource="sqlmap/User.xml" />
    		<!-- <mapper resource="test/lx/mybatis/mapper/UserMapper.xml"/> -->
    		<!-- 通过class引用mapper接口
    			 class:配置mapper接口的全限定名
    			 要求:需要mapper.xml和mapper.java同名并且在同一目录中
    		 -->
    		<!-- <mapper class="test.lx.mybatis.mapper.UserMapper"/> -->
    		<!-- 批量mapper配置
    			 通过package进行自动扫描包下边的mapper接口
    			 要求:需要mapper.xml和mapper.java同名并在同一目录中
    		 -->
    		<package name="test.lx.mybatis.mapper"/>
    	</mappers>

    输入和输出映射

    通过parameterType完成输入映射,通过resultType和resultMap完成输出映射。

    parameterType传递pojo包装对象

    可以自定义pojo包装类型扩展mapper接口输入参数的内容。
    需求:自定义查询条件查询用户信息,需要向statement输入查询条件,查询条件可以有user信息,商品信息......

    定义包装类型

    public class UserQueryVo {
    	//用户信息
    	private User user;
    	
    	//自定义User的扩展对象
    	private UserCustom userCustom;
    	//提供对应的getter和setter方法
            ......
    }

    mapper.xml

    <!-- 自定义查询条件查询用户信息 
      parameterType: 指定包装类型
      %${userCustom.username}%: userCustom是userQueryVo中的属性,通过OGNL获取属性的值
      -->
      <select id="findUserList" parameterType="userQueryVo" resultType="user">
         select * from user where username like '%${userCustom.username}%'
      </select>

    mapper.java

    //自定义查询条件查询用户信息
    	public List<User> findUserList(UserQueryVo userQueryVo) throws Exception;

    测试

    //通过包装类型查询用户信息
    	@Test
    	public void testFindUserList() throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		// 构造查询条件
    		UserQueryVo userQueryVo = new UserQueryVo();
    		UserCustom userCustom = new UserCustom();
    		userCustom.setUsername("小明");
    		userQueryVo.setUserCustom(userCustom);
    
    		List<User> list = userMapper.findUserList(userQueryVo);
    		System.out.println(list);
    	}

    异常

    如果parameterType中指定属性错误,会抛出异常,找不到getter方法:
    org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userCusto' in class …
    注意:如果将来和Spring整合后,不是通过getter方法来获取获取属性值,而是通过反射强读pojo的属性值。

    resultType

    指定输出结果的类型(pojo、简单类型、hashmap...),将SQL查询结果映射为Java对象。

    返回简单类型

    mapper.xml
    <!-- 输出简单类型
      功能:自定义查询条件,返回查询记录个数,通常用于实现查询分页
       -->
       <select id="findUserCount" parameterType="userQueryVo" resultType="int">
       	select count(*) from user where username like '%${userCustom.username}%'
       </select>
    mapper.java
    //查询用户返回记录个数
    	public int findUserCount(UserQueryVo userQueryVo) throws Exception;
    测试代码
    //返回查询记录总数
    	@Test
    	public void testFindUserCount() throws Exception{
    		SqlSession sqlSession  =sqlSessionFactory.openSession();
    		//创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		//构建查询条件
    		UserQueryVo userQueryVo = new UserQueryVo();
    		UserCustom userCustom = new UserCustom();
    		userCustom.setUsername("小明");
    		userQueryVo.setUserCustom(userCustom);
    		
    		int count = userMapper.findUserCount(userQueryVo);
    		System.out.println(count);
    				
    	}
    注意:如果查询记录结果集为一条记录且一列 才适合返回简单类型。

    resultMap(入门)

    resultType:指定输出结果的类型(pojo、简单类型、hashmap),将SQL查询结果映射为Java对象。
    使用resultType注意:sql查询的列名要和resultType指定pojo的属性相同,指定相同,属性方可映射成功。如果sql查询的列名要和resultType指定pojo的属性全部不相同,list中是无法创建pojo对象的。有几个属性对应相同,则能给对应相同的属性赋值。
    resultMap:将SQL查询结果映射为Java对象。如果SQL查询列名和最终要映射的pojo的属性名不一致,使用resultMap将列名和pojo的属性名做一个映射关系(列名和属性名映射配置)。

    resultMap配置

    <!-- 定义resultMap,列名和属性名映射配置
     id: mapper.xml中唯一标识
     type: 最终要映射的pojo类型
      -->
      <resultMap id="userListResultMap" type="user" >
    	  <!-- 列名
    	  id,username_,birthday_
    	  id:要映射结果集的唯一标识,称为主键
    	  column: 结果集的列名
    	  property:type指定pojo中的某个属性
    	  -->
    	  <id column="id_" property="id" />
          <!-- result就是普通列的映射配置 -->
          <result column="username_" property="username"/>
          <result column="birthday_" property="birthday"/>
      </resultMap>

    使用resultMap

     <!-- 使用resultMap作为结果映射
      resultMap: 如果引用resultMap的位置和resultMap的定义在同一个mapper.xml中,
      直接使用resultMap的id,如果不在同一个mapper.xml中,要在引用resultMap的id前边加namespace
       -->
      <select id="findUserListResultMap" parameterType="userQueryVo" resultMap="userListResultMap">
      	select id id_,username username_,birthday birthday_ from user where username like '%${userCustom.username}%'
      </select>

    mapper.java

    //查询用户,使用resultMap进行映射
    	public List<User> findUserListResultMap(UserQueryVo userQueryVo) throws Exception;

    动态SQL(重点)

    需求

    将自定义查询条件查询用户列表和查询用户列表总记录数改为动态SQL。

    if和where

     <!-- where标签相当于where关键字,可以自动除去第一个and -->
         <where>
            <!-- 如果userQueryVo中传入查询条件,在进行SQL拼接 -->
            <!-- test中userCustom.username表示从userQueryVo中读取属性值 -->
            <if test="userCustom!=null">
            	<if test="userCustom.username!=null and userCustom.username.trim().length() > 0">
            		and username like '%${userCustom.username.trim()}%'
            	</if>
            	<if test="userCustom.sex!=null and userCustom.sex!=''">
            		and sex = #{userCustom.sex}
            	</if>
            	<!-- 还可以添加更多的查询条件 -->
            	
            </if>
         </where>

    sql片段

    通过sql片段可以将通用的SQL语句抽取出来,单独定义,在其它的statement中可以引用SQL片段。
    即通用的SQL语句,常用的:where条件、查询列

    sql片段的定义

    <!-- 将用户查询条件定义为SQL片段
    	 建议对单表的查询条件单独抽取成SQL片段,提高公用性
    	 注意:不要讲where标签放在SQL片段,因为where条件中可能有多个SQL片段进行结合
    	  -->
    	  <sql id="query_user_where">
    	   		<!-- 如果userQueryVo中传入查询条件,在进行SQL拼接 -->
    	        <!-- test中userCustom.username表示从userQueryVo中读取属性值 -->
    	        <if test="userCustom!=null">
    	        	<if test="userCustom.username!=null and userCustom.username.trim().length() > 0">
    	        		and username like '%${userCustom.username.trim()}%'
    	        	</if>
    	        	<if test="userCustom.sex!=null and userCustom.sex!=''">
    	        		and sex = #{userCustom.sex}
    	        	</if>
    	        	<!-- 还可以添加更多的查询条件 -->
    	        	
    	        </if>
    	  </sql>

    引用sql片段

    在查询用户数据中引用
    <select id="findUserList" parameterType="userQueryVo" resultType="user">
    	     select * from user 
    	     <!-- where标签相当于where关键字,可以自动除去第一个and -->
    	     <where>
    	       <!-- 引用sql片段,如果sql片段和引用处不在同一个mapper 必须在前边加namespace. -->
    	       <include refid="query_user_where"></include>
    	       <!-- 下边还有很多其它的条件 -->
    	       <!-- <include refid="其它的sql片段"></include> -->
    	     </where>
    	  </select>
    在查询用户数据总数量中引用
     <!-- 输出简单类型
    	  功能:自定义查询条件,返回查询记录个数,通常用于实现查询分页
    	   -->
    	   <select id="findUserCount" parameterType="userQueryVo" resultType="int">
    	   	select count(*) from user 
    	   	<!-- where标签相当于where关键字,可以自动除去第一个and -->
    	     <where>
    	       <!-- 引用sql片段,如果sql片段和引用处不在同一个mapper 必须在前边加namespace. -->
    	       <include refid="query_user_where"></include>
    	       <!-- 下边还有很多其它的条件 -->
    	       <!-- <include refid="其它的sql片段"></include> -->
    	     </where>
    	   </select>

    foreach

    在statement中通过foreach遍历parameterType中的集合类型
    需求:假设根据多个用户id查询用户信息

    在userQueryVo中定义list<Integer> ids;

    在UserQueryVo中定义List<Integer> ids存储多个id
    public class UserQueryVo {
    	//用户信息
    	private User user;
    	
    	//自定义User的扩展对象
    	private UserCustom userCustom;
    	
    	//用户id集合
    	private List<Integer> ids;
           //添加对应的setter和getter方法
           ......
    }

    修改where语句

    使用foreach遍历list
    <!-- 根据id集合查询用户信息 -->
    	        	<!-- 最终拼接的效果:
    	        	SELECT id,username,birthday FROM USER WHERE username LIKE '%小明%' AND id IN (16,22,25)
    	        	collection: pojo中的表示集合的属性
    	        	open: 开始循环拼接的串
    	        	close: 结束循环拼接的串
    	        	item: 每次循环从集合中取到的对象
    	        	separator: 没两次循环中间拼接的串
    	        	 -->
    	        	<foreach collection="ids" open=" AND id IN (" close=")" item="id" separator=",">
    	        		#{id}
    	        	</foreach>
    	        	<!-- 
       	        	 SELECT id ,username ,birthday  FROM USER WHERE username LIKE '%小明%' AND (id = 16 OR id = 22 OR id = 25) 
    	        	 <foreach collection="ids" open=" AND id IN (" close=")" item="id" separator=" OR ">
    	        	 	id=#{id}
    	        	 </foreach>
    	        	 -->

    测试代码

    //id集合
    		List<Integer> ids = new ArrayList<Integer>();
    		ids.add(16);
    		ids.add(22);
    		userQueryVo.setIds(ids);
    
    		List<User> list = userMapper.findUserList(userQueryVo);

    最终Demo代码如下:(GitHub地址:https://github.com/LX1993728/mybatisDemo_1)

    UserDao
    package test.lx.mybatis.dao;
    
    import java.util.List;
    
    import test.lx.mybatis.po.User;
    
    /**
     * 用户DAO
     * 
     * @author lx
     * 
     */
    public interface UserDao {
    	// 根据id查询用户信息
    	public User findUserById(int id) throws Exception;
    
    	// 根据用户名称模糊查询用户列表
    	public List<User> findUserByUsername(String username) throws Exception;
    
    	// 插入用户
    	public void insertUser(User user) throws Exception;
    }
    UserDaoImpl
    package test.lx.mybatis.dao;
    
    import java.util.List;
    
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    
    import test.lx.mybatis.po.User;
    
    public class UserDaoImpl implements UserDao {
       private SqlSessionFactory sqlSessionFactory;
       
       //将SqlSessionFactory注入
       public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
    	   this.sqlSessionFactory = sqlSessionFactory;
       }
    	
    	public User findUserById(int id) throws Exception {
    		//创建SqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		
    		//根据id查询用户信息
    		User user = sqlSession.selectOne("test.findUserById", id);
    		sqlSession.close();
    		return user;
    	}
    
    	
    	public List<User> findUserByUsername(String username) throws Exception {
    		//创建SqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		List<User> list = sqlSession.selectList("test.findUserByName", username);
    		sqlSession.close();
    		return list;
    	}
    
    	
    	public void insertUser(User user) throws Exception {
    		//创建SqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		sqlSession.insert("test.insertUser", user);
    		sqlSession.commit();
    		sqlSession.close();
    	}
    
    }
    
    MyBatisFirst
    package test.lx.mybatis.first;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Date;
    import java.util.List;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Before;
    import org.junit.Test;
    
    import test.lx.mybatis.po.User;
    
    /**
     * mybatis入门程序
     * 
     * @author lx
     * 
     */
    public class MybatisFirst {
    	    // 会话工厂
    		private SqlSessionFactory sqlSessionFactory;
    		// 创建工厂
    		@Before
    		public void init() throws IOException {
    
    			// 配置文件(SqlMapConfig.xml)
    			String resource = "SqlMapConfig.xml";
    
    			// 加载配置文件到输入 流
    			InputStream inputStream = Resources.getResourceAsStream(resource);
    
    			// 创建会话工厂
    			sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
    		}
    
    		// 测试根据id查询用户(得到单条记录)
    		@Test
    		public void testFindUserById() {
    
    			// 通过sqlSessionFactory创建sqlSession
    
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    
    			// 通过sqlSession操作数据库
    			// 第一个参数:statement的位置,等于namespace+statement的id
    			// 第二个参数:传入的参数
    			User user = null;
    			try {
    				user = sqlSession.selectOne("test.findUserById", 1);
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    
    			System.out.println(user);
    
    		}
    		// 测试根据名称模糊查询用户(可能得到多条记录)
    		@Test
    		public void testFindUserByName() {
    			
    			// 通过sqlSessionFactory创建sqlSession
    			
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			// 通过sqlSession操作数据库
    			// 第一个参数:statement的位置,等于namespace+statement的id
    			// 第二个参数:传入的参数
    			List<User> list = null;
    			try {
    				//list = sqlSession.selectList("test.findUserByName", "小明");
    				list = sqlSession.selectList("test.findUserByName2", "%小明%");
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    			
    			System.out.println(list.size());
    		}
    		// 测试插入用户
    		@Test
    		public void testInsertUser() {
    			// 通过sqlSessionFactory创建sqlSession
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			//通过sqlSession操作数据库
    			//创建插入数据对象
    			User user = new User();
    			user.setUsername("一蓑烟雨");
    			user.setAddress("河南周口");
    			user.setBirthday(new Date());
    			user.setSex("1");
    			try {
    				sqlSession.insert("test.insertUser", user);
    				//需要提交事务
    				sqlSession.commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    			System.out.println(user.getId());
    		}
    		// 测试删除用户
    		@Test
    		public void testDeleteUser() {
    			// 通过sqlSessionFactory创建sqlSession
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			//通过sqlSession操作数据库
    			try {
    				sqlSession.delete("test.deleteUser", 28);
    				//需要提交事务
    				sqlSession.commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    		}
    		// 测试根据id更新用户(得到单条记录)
    		@Test
    		public void testUpdateUser() {
    			// 通过sqlSessionFactory创建sqlSession
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			
    			//通过sqlSession操作数据库
    			//创建更新数据库对象,要求必须包括id
    			User user= new User();
    			user.setId(28);
    			user.setUsername("任平生");
    			//凡是没有设置的属性都被当成了NULL进行赋值
    			//user.setBirthday(new Date());
    			user.setSex("1");
    			
    			try {
    				sqlSession.delete("test.updateUser", user);
    				//需要提交事务
    				sqlSession.commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				// 关闭sqlSession
    				sqlSession.close();
    			}
    		}
    
    }
    
    JdbcTest
    package test.lx.mybatis.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
     * jdbc的测试程序
     * 
     * @author lx
     * 
     */
    public class JdbcTest {
    	public static void main(String[] args) {
    		Connection connection = null;
    		//PreparedStatement是预编译的Statement,通过Statement发起数据库的操作
    		//PreparedStatement防止sql注入,执行数据库效率高
    		PreparedStatement preparedStatement = null;
    		ResultSet resultSet = null;
    		
    		try {
    			//加载数据库驱动
    			Class.forName("com.mysql.jdbc.Driver");
    			
    			//通过驱动管理类获取数据库链接
    			connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
    			//定义sql语句 ?表示占位符
    		String sql = "select * from user where username = ?" ;
    			//获取预处理statement
    			preparedStatement = connection.prepareStatement(sql);
    			//设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
    			preparedStatement.setString(1, "王五");
    			//向数据库发出sql执行查询,查询出结果集
    			resultSet =  preparedStatement.executeQuery();
    			//遍历查询结果集
    			while(resultSet.next()){
    				System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			//释放资源
    			if(resultSet!=null){
    				try {
    					resultSet.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if(preparedStatement!=null){
    				try {
    					preparedStatement.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if(connection!=null){
    				try {
    					connection.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    
    		}
    
    	}
    }
    
    UserMapper.java
    package test.lx.mybatis.mapper;
    
    import java.util.List;
    
    import test.lx.mybatis.po.User;
    import test.lx.mybatis.po.UserQueryVo;
    
    /**
     * 用户mapper
     * 
     * @author lx
     *
     */
    public interface UserMapper {
    
    	// 根据用户id查询用户信息
    	public User findUserById(int id) throws Exception;
    
    	// 根据用户姓名查询用户信息
    	public List<User> findUserByName(String username) throws Exception;
    
    	// 自定义查询条件查询用户信息
    	public List<User> findUserList(UserQueryVo userQueryVo) throws Exception;
    
    	// 查询用户,使用resultMap进行映射
    	public List<User> findUserListResultMap(UserQueryVo userQueryVo) throws Exception;
    
    	// 查询用户返回记录个数
    	public int findUserCount(UserQueryVo userQueryVo) throws Exception;
    
    	// 插入用户
    	public void insertUser(User user) throws Exception;
    
    	// 删除用户
    	public void deleteUser(int id) throws Exception;
    
    	// 修改用户
    	public void updateUser(User user) throws Exception;
    }
    
    UserMapper.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace命名空间,为了对SQL语句进行隔离,方便管理,mapper可开发dao方式,使用namespace有特殊作用 
    mapper代理开发时将namespace指定为mapper接口的全限定名 -->
    <mapper namespace="test.lx.mybatis.mapper.UserMapper">
    <!-- 在mapper.xml文件中配置很多的SQL语句,执行每个SQL语句时,封装为MappedStatement对象
    mapper.xml以statement为单位管理SQL语句
     -->
     
    	 <!-- 将用户查询条件定义为SQL片段
    	 建议对单表的查询条件单独抽取成SQL片段,提高公用性
    	 注意:不要讲where标签放在SQL片段,因为where条件中可能有多个SQL片段进行结合
    	  -->
    	  <sql id="query_user_where">
    	   		<!-- 如果userQueryVo中传入查询条件,在进行SQL拼接 -->
    	        <!-- test中userCustom.username表示从userQueryVo中读取属性值 -->
    	        <if test="userCustom!=null">
    	        	<if test="userCustom.username!=null and userCustom.username.trim().length() > 0">
    	        		and username like '%${userCustom.username.trim()}%'
    	        	</if>
    	        	<if test="userCustom.sex!=null and userCustom.sex!=''">
    	        		and sex = #{userCustom.sex}
    	        	</if>
    	        	<!-- 根据id集合查询用户信息 -->
    	        	<!-- 最终拼接的效果:
    	        	SELECT id,username,birthday FROM USER WHERE username LIKE '%小明%' AND id IN (16,22,25)
    	        	collection: pojo中的表示集合的属性
    	        	open: 开始循环拼接的串
    	        	close: 结束循环拼接的串
    	        	item: 每次循环从集合中取到的对象
    	        	separator: 没两次循环中间拼接的串
    	        	 -->
    	        	 <if test="ids != null and ids.size()>0">
    		        	<foreach collection="ids" open=" AND id IN (" close=")" item="id" separator=",">
    		        		#{id}
    		        	</foreach>
    	        	 </if>
    	        	<!-- 
       	        	 SELECT id ,username ,birthday  FROM USER WHERE username LIKE '%小明%' AND (id = 16 OR id = 22 OR id = 25) 
    	        	 <foreach collection="ids" open=" AND id IN (" close=")" item="id" separator=" OR ">
    	        	 	id=#{id}
    	        	 </foreach>
    	        	 -->
    	        	<!-- 还可以添加更多的查询条件 -->
    	        	
    	        </if>
    	  </sql>
    	 
    	 <!-- 定义resultMap,列名和属性名映射配置
    	 id: mapper.xml中唯一标识
    	 type: 最终要映射的pojo类型
    	  -->
    	  <resultMap id="userListResultMap" type="user" >
    		  <!-- 列名
    		  id,username_,birthday_
    		  id:要映射结果集的唯一标识,称为主键
    		  column: 结果集的列名
    		  property:type指定pojo中的某个属性
    		  -->
    		  <id column="id_" property="id" />
    	      <!-- result就是普通列的映射配置 -->
    	      <result column="username_" property="username"/>
    	      <result column="birthday_" property="birthday"/>
    	  </resultMap>
    	 
    	 <!-- 根据id查询用户信息 -->
    	 <!-- 
    	    id: 唯一标识一个statement
    	    #{}:表示一个占位符,如果#{} 中传入简单类型的参数,#{}中的名称随意
    	    parameterType: 输入参数的类型,通过#{}接收parameterType输入的参数
    	    resultType:输出结果类型,不管返回是多条还是单条,指定单条记录映射的pojo类型
    	  -->
    	  <select id="findUserById" parameterType="int" resultType="user">
    	     SELECT * FROM USER WHERE id=#{id};
    	  </select>
    	  
    	  <!-- 根据用户名称查询用户信息,可能返回多条 
    	  ${}:表示SQL的拼接,通过${}接收参数,将参数的内容不加任何修饰的拼接在SQL中
    	  -->
    	  <select id="findUserByName" parameterType="java.lang.String" resultType="test.lx.mybatis.po.User">
    	       select * from user where username like '%${value}%' 
    	  </select>
    	  
    	  <!-- <select id="findUserByName" parameterType="java.lang.String" resultType="test.lx.mybatis.po.User">
    	       select * from user where username like #{username} 
    	  </select> -->
    	  
    	  <!-- 自定义查询条件查询用户信息 
    	  parameterType: 指定包装类型
    	  %${userCustom.username}%: userCustom是userQueryVo中的属性,通过OGNL获取属性的值
    	  -->
    	  <select id="findUserList" parameterType="userQueryVo" resultType="user">
    	     select * from user 
    	     <!-- where标签相当于where关键字,可以自动除去第一个and -->
    	     <where>
    	       <!-- 引用sql片段,如果sql片段和引用处不在同一个mapper 必须在前边加namespace. -->
    	       <include refid="query_user_where"></include>
    	       <!-- 下边还有很多其它的条件 -->
    	       <!-- <include refid="其它的sql片段"></include> -->
    	     </where>
    	  </select>
    	  
    	  <!-- 使用resultMap作为结果映射
    	  resultMap: 如果引用resultMap的位置和resultMap的定义在同一个mapper.xml中,
    	  直接使用resultMap的id,如果不在同一个mapper.xml中,要在引用resultMap的id前边加namespace
    	   -->
    	  <select id="findUserListResultMap" parameterType="userQueryVo" resultMap="userListResultMap">
    	  	select id id_,username username_,birthday birthday_ from user where username like '%${userCustom.username}%'
    	  </select>
    	  
    	  <!-- 输出简单类型
    	  功能:自定义查询条件,返回查询记录个数,通常用于实现查询分页
    	   -->
    	   <select id="findUserCount" parameterType="userQueryVo" resultType="int">
    	   	select count(*) from user 
    	   	<!-- where标签相当于where关键字,可以自动除去第一个and -->
    	     <where>
    	       <!-- 引用sql片段,如果sql片段和引用处不在同一个mapper 必须在前边加namespace. -->
    	       <include refid="query_user_where"></include>
    	       <!-- 下边还有很多其它的条件 -->
    	       <!-- <include refid="其它的sql片段"></include> -->
    	     </where>
    	   </select>
    	   
    	  <!-- 添加用户 
    	   parameterType:输入参数的类型,User对象包括username,birthday,sex,address
    	   #{}接收pojo数据,可以使用OGNL解析出pojo的属性值
    	   #{username}表示从parameterType中获取pojo的属性值
    	   <selectKey>:用于进行主键返回,定义了主键值的SQL
    	   order:设置selectKey标签中SQL的执行顺序,相对于insert语句而言
    	   keyProperty: 将主键设置到哪个属性上
    	   resultType:select LAST_INSERT_ID()的结果类型
    	  -->
    	  <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
    	     <selectKey keyProperty="id" order="AFTER" resultType="int">
    	        select LAST_INSERT_ID()
    	     </selectKey>
    	 	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
    	  </insert>
    	  
    	  <!-- mysql的uuid()函数生成主键 -->
    	 <!--  <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
    	     <selectKey keyProperty="id" order="BEFORE" resultType="string">
    	        select uuid()
    	     </selectKey>
    	 	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
    	  </insert> -->
    	  
    	  <!-- oracle
    	         在执行insert之前执行select 序列.nextval() from dual取出序列最大值,将值设置到user对象的id属性中
    	   -->
    	 <!--  <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
    	     <selectKey keyProperty="id" order="BEFORE" resultType="int">
    	        select 序列.nextval() from dual
    	     </selectKey>
    	 	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
    	  </insert> -->
    	  
    	  <!-- 用户删除 -->
    	  <delete id="deleteUser" parameterType="int">
    	   delete from user where id=#{id}
    	  </delete>
    	  <!-- 用户更新
    	  	要求:传入的user对象包括id属性值
    	   -->
    	   <update id="updateUser" parameterType="test.lx.mybatis.po.User">
    	   update user set username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    	   </update>
    </mapper>
    User.java
    package test.lx.mybatis.po;
    
    import java.util.Date;
    
    /**
     * 用户PO类
     * 
     * @author lx
     * 
     */
    public class User {
    	private int id;
    	private String username; // 用户姓名
    	private String sex; // 性别
    	private Date birthday; // 生日
    	private String address; // 地址
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getUsername() {
    		return username;
    	}
    
    	public void setUsername(String username) {
    		this.username = username;
    	}
    
    	public String getSex() {
    		return sex;
    	}
    
    	public void setSex(String sex) {
    		this.sex = sex;
    	}
    
    	public Date getBirthday() {
    		return birthday;
    	}
    
    	public void setBirthday(Date birthday) {
    		this.birthday = birthday;
    	}
    
    	public String getAddress() {
    		return address;
    	}
    
    	public void setAddress(String address) {
    		this.address = address;
    	}
    
    	@Override
    	public String toString() {
    		return "User [id=" + id + ", username=" + username + ", sex=" + sex
    				+ ", birthday=" + birthday + ", address=" + address + "]";
    	}
    
    }
    
    UserCustom
    package test.lx.mybatis.po;
    /**
     * User的扩展类型
     * @author liuxun
     *
     */
    public class UserCustom extends User {
        //添加一些扩展类型
    }
    
    UserQueryVo
    package test.lx.mybatis.po;
    
    import java.util.List;
    
    /**
     * 包装类型,将来在使用时从页面传递到controller、service、mapper
     * @author liuxun
     *
     */
    public class UserQueryVo {
    	//用户信息
    	private User user;
    	
    	//自定义User的扩展对象
    	private UserCustom userCustom;
    	
    	//用户id集合
    	private List<Integer> ids;
    
    	public User getUser() {
    		return user;
    	}
    
    	public void setUser(User user) {
    		this.user = user;
    	}
    
    	public UserCustom getUserCustom() {
    		return userCustom;
    	}
    
    	public void setUserCustom(UserCustom userCustom) {
    		this.userCustom = userCustom;
    	}
    
    	public List<Integer> getIds() {
    		return ids;
    	}
    
    	public void setIds(List<Integer> ids) {
    		this.ids = ids;
    	}
    	
    	
    }
    
    User.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命名空间,为了对SQL语句进行隔离,方便管理,mapper可开发dao方式,使用namespace有特殊作用 
    mapper代理开发时将namespace指定为mapper接口的全限定名 -->
    <mapper namespace="test">
    <!-- 在mapper.xml文件中配置很多的SQL语句,执行每个SQL语句时,封装为MappedStatement对象
    mapper.xml以statement为单位管理SQL语句
     -->
     
     <!-- 根据id查询用户信息 -->
     <!-- 
        id: 唯一标识一个statement
        #{}:表示一个占位符,如果#{} 中传入简单类型的参数,#{}中的名称随意
        parameterType: 输入参数的类型,通过#{}接收parameterType输入的参数
        resultType:输出结果类型,指定单条记录映射的pojo类型
      -->
      <select id="findUserById" parameterType="int" resultType="test.lx.mybatis.po.User">
         SELECT * FROM USER WHERE id=#{id};
      </select>
      
      <!-- 根据用户名称查询用户信息,可能返回多条 
      ${}:表示SQL的拼接,通过${}接收参数,将参数的内容不加任何修饰的拼接在SQL中
      -->
      <select id="findUserByName" parameterType="java.lang.String" resultType="test.lx.mybatis.po.User">
           select * from user where username like '%${value}%' 
      </select>
      
      <select id="findUserByName2" parameterType="java.lang.String" resultType="test.lx.mybatis.po.User">
           select * from user where username like #{username} 
      </select>
      
      <!-- 添加用户 
       parameterType:输入参数的类型,User对象包括username,birthday,sex,address
       #{}接收pojo数据,可以使用OGNL解析出pojo的属性值
       #{username}表示从parameterType中获取pojo的属性值
       <selectKey>:用于进行主键返回,定义了主键值的SQL
       order:设置selectKey标签中SQL的执行顺序,相对于insert语句而言
       keyProperty: 将主键设置到哪个属性上
       resultType:select LAST_INSERT_ID()的结果类型
      -->
      <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
         <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID()
         </selectKey>
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert>
      
      <!-- mysql的uuid()函数生成主键 -->
     <!--  <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
         <selectKey keyProperty="id" order="BEFORE" resultType="string">
            select uuid()
         </selectKey>
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert> -->
      
      <!-- oracle
             在执行insert之前执行select 序列.nextval() from dual取出序列最大值,将值设置到user对象的id属性中
       -->
     <!--  <insert id="insertUser" parameterType="test.lx.mybatis.po.User">
         <selectKey keyProperty="id" order="BEFORE" resultType="int">
            select 序列.nextval() from dual
         </selectKey>
     	 INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
      </insert> -->
      
      <!-- 用户删除 -->
      <delete id="deleteUser" parameterType="int">
       delete from user where id=#{id}
      </delete>
      <!-- 用户更新
      	要求:传入的user对象包括id属性值
       -->
       <update id="updateUser" parameterType="test.lx.mybatis.po.User">
       update user set username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
       </update>
    </mapper>
    db.properties
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis
    jdbc.username=root
    jdbc.password=root
    
    log4j.properties
    # Global logging configuration\uff0c\u5efa\u8bae\u5f00\u53d1\u73af\u5883\u4e2d\u8981\u7528debug
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    
    SqlMapConfig.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文件
                 在properties标签中配置属性值
         -->
         <properties resource="db.properties">
             <!-- <property name="" value=""/> -->
         </properties>
         
         <settings>
            <setting name="cacheEnabled" value="true"/>
         </settings>
         <!-- 定义别名 -->
         <typeAliases>
         <!-- 
    	     单个别名定义
    	     alias:别名, type:别名映射类型
    	     <typeAlias type="test.lx.mybatis.po.User" alias="user"/>
          -->
          <!-- 批量别名定义
    		 指定包路径,自动扫描包内的pojo,定义别名,别名默认为类名(首字母小写或大写)      		
           -->
           <package name="test.lx.mybatis.po"/>
         </typeAliases>
         
    	<!-- 和Spring整合后environments配置将废除 -->
    	<environments default="development">
    		<environment id="development">
    			<!-- 使用jdbc事务管理 -->
    			<transactionManager type="JDBC" />
    			<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>
    
    	<!-- 加載mapper映射
    	如果和Spring整合后,可以使用整合包中的mapper扫描器,到那时此处的mapper就不用配置了
    	-->
    	<mappers>
    	     <!-- 通过resource映入mapper的映射文件 -->
    		<mapper resource="sqlmap/User.xml" />
    		<!-- <mapper resource="test/lx/mybatis/mapper/UserMapper.xml"/> -->
    		<!-- 通过class引用mapper接口
    			 class:配置mapper接口的全限定名
    			 要求:需要mapper.xml和mapper.java同名并且在同一目录中
    		 -->
    		<!-- <mapper class="test.lx.mybatis.mapper.UserMapper"/> -->
    		<!-- 批量mapper配置
    			 通过package进行自动扫描包下边的mapper接口
    			 要求:需要mapper.xml和mapper.java同名并在同一目录中
    		 -->
    		<package name="test.lx.mybatis.mapper"/>
    	</mappers>
    </configuration>
    
    UserDaoImplTest
    package test.lx.mybatis.dao;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Before;
    import org.junit.Test;
    
    import test.lx.mybatis.po.User;
    
    public class UserDaoImplTest {
    	// 会话工厂
    	private SqlSessionFactory sqlSessionFactory;
    	
    	//创建工厂
    	@Before
    	public void init() throws IOException{
    		//配置文件(SqlMapConfig.xml)
    		String resource = "SqlMapConfig.xml";
    		
    		//加载配置文件到输入流
    		InputStream inputStream = Resources.getResourceAsStream(resource);
    		
    		//创建会话工厂
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);		
    	}
    	
    	@Test
    	public void testFindUserById() throws Exception{
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    		
    		User user = userDao.findUserById(1);
    		System.out.println(user);
    	}
    	
    }
    
    UserMapperTest.java
    package test.lx.mybatis.mapper;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Before;
    import org.junit.Test;
    
    import test.lx.mybatis.po.User;
    import test.lx.mybatis.po.UserCustom;
    import test.lx.mybatis.po.UserQueryVo;
    
    public class UserMapperTest {
    
    	// 会话工厂
    	private SqlSessionFactory sqlSessionFactory;
    
    	// 创建工厂
    	@Before
    	public void init() throws IOException {
    		// 配置文件(SqlMapConfig.xml)
    		String resource = "SqlMapConfig.xml";
    
    		// 加载配置文件到输入流
    		InputStream inputStream = Resources.getResourceAsStream(resource);
    
    		// 创建会话工厂
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    	}
    
    	@Test
    	public void testFindUserById() throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		User user = userMapper.findUserById(1);
    		System.out.println(user);
    		sqlSession.close();
    	}
    
    	@Test
    	public void testFindUserByUsername() throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		List<User> list = userMapper.findUserByName("小明");
    		sqlSession.close();
    		System.out.println(list);
    	}
    	
    	@Test
    	public void testInsertUser() throws Exception{
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
           
    		//插入对象
    		User user = new User();
    		user.setUsername("一蓑烟雨任平生");
    		userMapper.insertUser(user);
    		sqlSession.commit();
    		sqlSession.close();
    		System.out.println(user);
    	}
    
    	//通过包装类型查询用户信息
    	@Test
    	public void testFindUserList() throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		// 构造查询条件
    		UserQueryVo userQueryVo = new UserQueryVo();
    		UserCustom userCustom = new UserCustom();
    		userCustom.setUsername("   小明");
    		userCustom.setSex("1");
    		userQueryVo.setUserCustom(userCustom);
    		
    		//id集合
    		List<Integer> ids = new ArrayList<Integer>();
    		ids.add(16);
    		ids.add(22);
    		userQueryVo.setIds(ids);
    
    		List<User> list = userMapper.findUserList(userQueryVo);
    		
    		sqlSession.close();
    		
    		System.out.println(list);
    	}
    	
    	//使用resultMap进行结果映射
    	@Test
    	public void testFindUserListResultMap() throws Exception {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		// 创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		// 构造查询条件
    		UserQueryVo userQueryVo = new UserQueryVo();
    		UserCustom userCustom = new UserCustom();
    		userCustom.setUsername("小明");
    		userQueryVo.setUserCustom(userCustom);
    		
    		List<User> list = userMapper.findUserListResultMap(userQueryVo);
    		sqlSession.close();
    		System.out.println(list);
    	}
    	
    	//返回查询记录总数
    	@Test
    	public void testFindUserCount() throws Exception{
    		SqlSession sqlSession  =sqlSessionFactory.openSession();
    		//创建代理对象
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		//构建查询条件
    		UserQueryVo userQueryVo = new UserQueryVo();
    		UserCustom userCustom = new UserCustom();
    		userCustom.setUsername("小明");
    		userQueryVo.setUserCustom(userCustom);
    		
    		int count = userMapper.findUserCount(userQueryVo);
    		sqlSession.close();
    		System.out.println(count);
    				
    	}
    }
    


    展开全文
  • 程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。 2、MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集...

    从今天开始以后每天3道面试题
    1、什么是 Mybatis?
    1、Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时
    只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
    2、MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 3、通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返回 result 的过程)。
    2、Mybaits 的优点:
    1、基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML标签,支持编写动态 SQL 语句,并可重用。
    2、与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
    3、很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要JDBC 支持的数据库 MyBatis 都支持)。
    4、能够与 Spring 很好的集成;
    5、提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
    3、MyBatis 框架的缺点:
    1、SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL 语句的功底有一定要求。
    2、SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

    展开全文
  • 优点开发成本低,简单、快速、方便,一套代码几乎不用怎么修改就可以同时打包ios、android安装包。相比于App原生开发来说,开发成本要低很多 目前很多第三方SDK也越来越多的开放h5端的接口,生态环境比较...



    h5(HBuilderX+mui)开发App

    一、h5开发App分析

    • 优点:
    1. 开发成本低,简单、快速、方便,一套代码几乎不用怎么修改就可以同时打包ios、android安装包。相比于App原生开发来说,开发成本要低很多
    2. 目前很多第三方SDK也越来越多的开放h5端的接口,生态环境比较活跃。
    3. h5开发App,采用的是web前端的html5技术,各方面应用技术栈成熟,资料齐全。
    • 缺点:
    1. h5开发App与原生的相比,说到底相当于是将h5界面嵌入到webview中,会存在页面渲染不及原生流畅。如果接触过cordova的朋友,可以研究一下coedova打包完vue成App后代码,其实就是讲h5部分的代码嵌入到原生的activity并通过webview加载。
    2. 开发工具框架之类的太多,如果一个不熟悉前端的新人过来,几乎不知道该如何下手。
      所以对于一些业务逻辑简单,没有复杂的操作处理和与硬件交互的应用,h5开发再合适不过。对一些重型的应用还是原生的为主,h5为辅助也是一个很好的搭配。

    二、技术选择

    • cordova + vue:
      App整体使用vue项目来编写代码,使用Cordova来打包vue项目成App。
      参考:
      (不过由于Cordova的配置等比较繁琐,本人不太推荐这一种方案)
    • HBuilderX + h5:
      App整体采用html5,以及mui框架来实现,通过HBuilderX来打包成App。
      相比于上一种Cordova打包方式,本人比较推荐这一种方案,不需要很繁琐的配置,并且开发过程中的调试,相信就凭这一点很多开发者就知道该怎么选择了。下面我主要就讲解第二种方案。

    三、h5开发App实操环节

    1.工具准备

    • HBuilderX:
      下载链接:https://www.dcloud.io/hbuilderx.html
      下载完直接解压打开HBuilderX.exe即可
      在这里插入图片描述不得不说,HBuilderX这一版本(2.4.6.20191210)的用户体验要比之前好很多。

    注意: 很多朋友可能一直在使用老版本的HBuilder,但是最近HBuilder出现了:ios手机调试遇到----“安装失败 return code=-402620395,请手动安装F:\develop_tools\HBuilder\plugins\com.pandora.tools.android_1.0.0.201808130227\base\iPhone_base.ipa到手机上(可使用iTools安装),并重新运行真机调试”—的问题,并且这个问题官方也没有给出解决方案,所以趁现在项目还不是很多的时候,就转移到HBuilderX上吧

    2.创建移动端项目

    (1). 新建项目

    文件 -> 新建 -> 项目:
    在这里插入图片描述
    对于项目来说,尽可能在使用最少的框架来完成项目的整体业务,这样方便后期的维护。本人选用mui框架的理由是:mui框架里面,在使用很多控件的时候,会优先调用手机原生的控件,比如说弹出框等等,在很大程度上保证了App使用的流畅性。

    (2). 项目目录结构

    新创建的项目如下:1是mui框架自带的资源,包含css,js,和字体库。2是项目的默认首页。3是App项目的基础配置,包括App的名称,logo,秘钥等等。
    抛开第3部分,其实这就是一个完完全全的html项目,里面都是遵循html的写法,所以这也就是为什么现在称这种开发方式为h5开发App。
    在这里插入图片描述

    (3). 代码编写及运行

    1. 在index.html下面添加如下代码:
      <p style="text-align: center;color: #FF0000;font-size: 1.875rem;margin-top: 18.75rem;">Hello World</p>
      在这里插入图片描述2. 手机调试查看效果:
      用USB连接线将手机和电脑连接,然后点击 运行 -> 运行到手机或模拟器 -> (你自己的设备)
      (如果这个地方不显示你的手机,可以尝试把usb拔了再试试,如果是android手机需要开启开发者模式。我是下了手机助手:爱思助手,比较方便检查手机和电脑连接)
      运行效果如下:
      在这里插入图片描述
      到这一步,基本上是已经完成App的开发与调试阶段了。剩下的就是将App打包出来,android是打包成Apk文件,ios是打包成ipa文件,然后分别安装到对应的手机上。

    (4). App的打包与上架

    1.App的打包:

    点击 发行 -> 原生App-云打包
    在这里插入图片描述
    2. 生成上图中需要的证书秘钥等:
    1). Android端:
    打开cmd窗口在里面输入:
    keytool -genkey -alias domekey -keyalg RSA -keysize 1024 -keypass pwd123456 -validity 3500 -keystore c:\key\dome.keystore

    参数解析如下:
    -alias 后面的 domekey 是密钥别名(证书别名),可自己修改
    -keypass 后面的 pwd123456 ,可自己修改
    会生成密钥文件dome.keystore,存在 c:\key\dome.keystore ,如果你C盘没有key文件夹,要新建一个,不然会报错。
    -validity 后面的 3500 ,是有效期,3500天,按天数算

    然后会出现
    在这里插入图片描述填好后,最后 填 Y 回车确认,就可以了,生成的dome.keystore,文件在C盘c:\key\下。
    生成好之后便可把证书及秘钥信息填写到上面,之后便可以开始打包了。

    2). ios端:
    参考:https://www.jianshu.com/p/e6b86bef7a90
    个人推荐一个软件:appuploader。支持生成苹果个人免费证书,唯一的遗憾就是这个软件是只有一个月的免费试用期。

    3). 使用公共证书:
    如果觉得生成私人证书比较麻烦,只是想打包测试一下,可以选择使用公共的测试证书来进行打包。但是如果想要把App上架,就必须使用私人证书。

    3.查看打包状态:
    点击 发行 -> 查看云打包状态
    打包完成之后,便可将打包好的App文件下载下来。Android端可以直接安装,ios端相对麻烦一点,没有办法直接安装。
    后续关于ios端如何推广安装,目前本人资料还未完全整理好,敬请谅解。

    展开全文
  • MyBatis(1)Mybatis是一个半ORM...程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。(2)MyBatis 可以使用XML 或注解来配置和映射原生信息,将POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码...
  • 1、使用jdbc程序使用原生态的jdbc进行开发存在很多弊端,优点是执行效率高,mybatis弥补了jdbc的缺陷。 2、mybatis的架构(重点)。 3、mybatis的入门程序(重点)。  实现数据的查询、添加、修改、删除 4、mybatis...
  • Quikapp快应用开发入门

    2018-03-26 14:08:00
    快应诞生背景 微信的小程序使得很多原来需要调动APP的场景不复存在,正式由于微信小程序的冲击,3月20日,华为...“快应用”使用前端技术栈开发与原生渲染,兼具H5页面和原生应用的双重优点。 快应用使用场景 ...
  • (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句 本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写 生态sql,可以严格控制...
  • 程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。 (2)MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数 据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取...
  • 程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。 (2)MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数 据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取...
  • 程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。 (2)MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。...
  • 3 月 20 日,中国大饭店,国内十家手机厂商:小米、华为、OPPO、vivo、魅族、金立、中兴、...“快应用” 使用前端技术栈开发,原生渲染,同时具备 H5 页面和原生应用的双重优点。“快应用” 框架深度集成进各厂商...
  • “快应用” VS “微信小程序” ...快应用是由9家厂商联合打造,使用前端技术栈开发、原生渲染,同时具备H5页面和原生应用的双重优点。 说直白点:就是用户无需下载 apk,直接点开就可以使用。 九大手机厂商为什...
  • 几款移动框架比较

    2019-03-22 20:40:13
    开源免费,社区生态成熟,插件丰富 支持离线场景应用 开发工具选择空间大 缺点 只提供基础访问设备的接口,需要自己搭配其他UI框架和JavaScript框架来搭配 Ionic 优点 基于 Cordova 漂亮的界面,追求性能,...
  • jdbc是Java提供的原生态接口,操作数据库的唯一技术 缺点:重复写代码,代码写死,耦合性高,开发效率低换数据库比较苦难 优点:运行速度最快,所有操作数据库的技术底层都是jdbc写的 jpa:  java persistence ...
  • 优点: 使用上手容易,开发成本低,跨平台使用 缺点: 使用体验差,网络要求高 原生app 适合场景:功能丰富,体验要求高,企业级应用 优点: 使用体验好,功能强大 缺点: 使用门槛相对高,技术要求高,开发成本高,不能...
  • React vue区别

    2021-03-31 23:11:03
    适用于web端,原生APP开发,侧重于大型项目应用 渲染速度更快,体积更小 更大的生态圈,更多工具支持 拓展的HTML语法渲染 jsx渲染 react的优点:render函数支持闭包,import的组件在render中可以直接调用 ...
  • 生态环境差 社区不大 报错不明显,适合单人开发或者中小型项目 uni-app 优点: uni-app是一套可以适用于多端的开源框架 uni-app扩展能力强 学习成本低,语法是vue的,组件和api是小程序的 扩展能力强使用HbuildX...
  • java - Mybatis框架

    2020-12-22 20:18:57
    Mybatis是一个半ORM(对象关系映射)的持久层框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程,使用时直接编写原生态sql。 优点: 1:...
  • 自学Vue第一天

    2019-09-22 11:25:45
    通过IOS或Android语言开发的 hybridAPP -> webapp + 原生APP 什么是Vue Vue有点 两个核心点: 响应式 组件 其他优点: 易用 虚拟DOM 双向数据绑定 体积小 生态圈繁荣、学习成本低 SEO -> 搜索引擎优化## 标题...
  • Spring与SpringBoot-01

    2021-03-16 22:38:39
    Spring 的生态Spring5 重大升级为什么用 SpringBootSpring Boot优点Spring Boot 缺点时代背景微服务分布式分布式的困难分布式的解决云原生上云的困难上云的解决如何学习SpringBoot官网文档架构 Spring 能做什么 ...
  • Node.js的包管理器npm(Node Package Manager)是一个开源库原生态系统,开发Node.js骨架型应用程序具有快速、高效等优点。为了开发Node.js应用程序,需要先对Node.js进行环境配置。Node.js的环境配置分为本地配置和...
  • 快应用和小程序各自的优势

    千次阅读 2019-03-08 17:54:51
    “快应用” 使用前端技术栈开发,原生渲染,同时具备 H5 页面和原生应用的双重优点。“快应用” 框架深度集成进各厂商手机系统中,可以在操作系统层面实现用户需求与应用服务间的无缝连接,提升用户的使用体验...
  • 大家都知道Python的优点开发效率高,使用方便,C++则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C++...原生态导出(推荐学习:Python视频教程)Python解释器就是用C实现,因此只要我们的C++的数据...
  • 大家都知道Python的优点开发效率高,使用方便,C 则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C ...原生态导出(推荐学习:Python视频教程)Python解释器就是用C实现,因此只要我们的C 的数据...
  • 前言大家都知道Python的优点开发效率高,使用方便,C++则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C++代码,...原生态导出Python解释器就是用C实现,因此只要我们的C++的数据结构能让Python认...
  • 大家都知道Python的优点开发效率高,使用方便,C++则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C++代码,或是在...1 原生态导出 Python解释器就是用C实现,因此只要我们的C++的数据结构能让Pyt...
  • Vue课堂笔记

    2019-11-20 19:30:55
    通过IOS或Android语言开发的 hybridAPP -> webapp + 原生APP 什么是Vue Vue有点 两个核心点: 响应式 组件 其他优点: 易用 虚拟DOM 双向数据绑定 体积小 生态圈繁荣、学习成本低 SEO -> 搜索引擎优化 ...
  • python调用C++

    2018-09-29 18:01:00
    前言 大家都知道Python的优点开发效率高,使用方便,C++则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C++代码,或是...原生态导出 Python解释器就是用C实现,因此只要我们的C++的数据结构能让P...

空空如也

空空如也

1 2 3 4
收藏数 76
精华内容 30
关键字:

原生态开发优点