1.加载数据库驱动
2.创建并获取数据库连接对象connection
3.通过连接对象获取会话对象statement
4.编写sql语句 5.如果有参数的话需要通过Statement设置参数
5.执行sql语句并获取结果
6.关闭资源
上述是最原始的JDBC操作数据库的方式,有以下问题:
1.和JBDC相比消除了JDBC大量冗余的代码,不需要手动开关连接
2.MyBatis的sql语句在xml文件里面编写,改变sql语句不再需要重新编译
public class UserTest {
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
}
1. parameterType
1. 简单数据类型
int double类型 String类型
简单的写法:java.lang.Integer --> int integer Int Integer 都可以,框架提供简写的方式。
2. POJO(JavaBean实体类)对象类型,默认是不能简写,可以配置。
User对象
2. resultType
1. 返回简单数据类型
int double long String
2. 返回POJO数据类型
返回User对象类型
3. resultMap结果类型
resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,
resultMap实质上还需要将查询结果映射到pojo对象中。 resultMap可以实现将查询结果映射为复杂类型的
pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
#{}是占位符:动态解析 -> 预编译 -> 执行
${}是拼接符:动态解析 -> 编译 -> 执行
预编译可以类比java类的编译,java类被编译成class文件,载入虚拟机,载入虚拟机的字节码文件可以先被编译成机器吗,那么在执行某行代码的时候就可以直接执行编译后的机器码,而不用从字节码开始编译再执行,那么执行效率就高了。这也是为啥热机状态比冷机状态可以抗更多负载的原因。
sql的预编译也是一样的道理,在执行前就编译好,等执行时直接取编译结果去执行。省去编译时间。sql预编译后会在参数位置用占位符表示
预编译:数据库驱动在发送sql和参数到DBMS之前,先对sql语句进行编译处理,之后DBMS则可以直接对sql进行处理,不需要再次编译,提高了性能。这一点mybatis 默认情况下,将对所有的 sql 进行预编译处理。
预编译可以将多个操作步骤合并成一个步骤,一般而言,越复杂的sql,编译程度也会复杂,难度大,耗时,费性能,而预编译可以合并这些操作,预编译之后DBMS[[可以省去编译直接运行sql。
预编译语句可以重复利用。把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。
1.能用 #{} 的地方就用 #{},尽量少用 ${}
2.表名作参数,或者order by 排序时用 ${}
3.传参时参数使用@Param("")注解,@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值(相当于又加了一层密), 正确的将参数传入sql语句中(一般通过#{}的方式,${}会有sql注入的问题)
会有and问题
自动剔除and or
update user
username = #{username} ,
address = #{address}
where id = #{id}
批量删除
delete from user where id in
#{id}
批量添加
insert into user(id,username,birthday,sex,address) values
(null,#{user.username},#{user.birthday},#{user.sex},#{user.address})
一对一
SELECT s.id,s.Sname,t.Tname FROM student s,teacher t where s.t_id = t.id
一对多
SELECT teacher.id,teacher.name,student.name FROM teacher
LEFT JOIN student on student.t_id = teacher.id
以上有两种写法来表示查询信息,分别是链表查询和分步查询的方法。那么既然我么能用一个SQL语句能够执行完,那为什么还要分开来写呢?
原因很简单:同学们可以发现如果我们把他们连在一起那么他们就是一个多表查询语句,如果不放在一起执行,那那就是单独一个表的查询语句。但是这需要我们设置mybatis的延迟加载(懒加载)
分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoding:当开启时,任何方式的调用都会加载该对象的所有属性。否则,该属性会按需加载
此时就可以实现按需加载,需要获取的数据是什么,就只会执行相应的sql.此时会通过association和collection中的fetchType属性设置当前的分步查询是否使用懒加载
fetchType=“lazy(延迟加载) | eager(立即加载)”
@Insert:添加 @Update:修改 @Delete:删除 @Select:查询 @Result:实现结果集封装 @Results:可以和@Result一起使用,封装多个结果集 @One:实现一对一和多对一的结果集封装 @Many:实现一对多结果级封装
事务有四个特性:一致性、持久性、原子性、隔离性
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession(true); //关闭
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
我们知道使用mybatis的时候,一般是一个接口对应一
public interface UserDao {
public List findAll();
}
个xml文件,那么我们的mybatis是怎样通过这个接口来调用并且xml当中的方法的呢?
下面我么简单的说一下mybatis的工作流程
1.项目启动时根据xml文件路径读取所有的xml信息,到map中存储,map的key可以定义为类名+方法名。
2.编写代理类,代理类负责根据被代理的类名+方法名,读取对应的sql配置,然后根据入参,拼接解析出完整sql,然后交给jdbc去执行,最后将返回数据转换成接口返回值的对象做返回。
3.根据dao的包路径读取所有的需要代理的dao对象,利用上面第二条写的代理类来循环为每个dao创建代理类。
public class MybatisProxy implements InvocationHandler {
private final static String URL = "jdbc:mysql://localhost:3306/student";
private final static String ROOT = "root";
private final static String PASSWORD = "2020";
private final static Map XML_MAP_SQL = new HashMap();
//加载被代理的对象,这里我们需要读取xml配置文件
static {
//这里我们模拟读取xml文件当中的方法名和sql语句
XML_MAP_SQL.put("findAll","select * from user");
}
// 要求代理对象要完成的功能
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
// 从map中读取
String sql = MybatisProxy.XML_MAP_SQL.get(name);
// 执行目标类当中的方法,在这里我们使用jdbc实现
search(sql);
return null;
}
// jdbc执行的方法
public void search(String sql) throws Exception {
Class.forName("com.mysql.jdbc.Driver"); //加载驱动
Connection connection = (Connection) DriverManager.getConnection(URL,ROOT,PASSWORD); //建立和保持数据库的连接
Statement statment = (Statement) connection.createStatement(); //创建statment
ResultSet set = statment.executeQuery(sql);
while (set.next()) {
System.out.print(set.getString("id")+" ");
System.out.print(set.getString("name")+" " );
System.out.print(set.getString("phone")+" " );
System.out.println(" ");
}
statment.close();
connection.close();
}
//该方法并不是固定的,但是内部的Proxy类的创建是核心
public Object getProxyInstance(Class clazz) {
// TODO Auto-generated method stub
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz}, this);
}
}
public class Test {
public static void main(String[] args) {
MybatisProxy mybatisProxy = new MybatisProxy();
UserDao userDao = (UserDao) mybatisProxy.getProxyInstance(UserDao.class);
userDao.findAll();
}
}