1. JDBC连接数据库
基础回顾:在咱们的JAVA里面怎么连接数据库?
首先引入MySQL连接驱动maven依赖:
<!-- mysql driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
@SpringBootTest
class MyJDBCTest {
/**
* @Description: jdbc test
* @Author create by mamba
* @Date 2020/7/7 11:18
* @param {}
*/
@Test
void myJDBC(){
Connection connection = null;
Statement statement = null;
Saying saying = new Saying();
try{
// register JDBC Driver
Class.forName("com.mysql.jdbc.Driver");
// open connection
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/commentsystem?serverTimezone=UTC", "root", "607918");
statement = connection.createStatement();
String sql = "select * from saying where 1=1";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
Integer sayingId = resultSet.getInt("saying_id");
String author = resultSet.getString("author");
String sayingContent = resultSet.getString("sayingContent");
String likes = resultSet.getString("likes");
Date createTime = resultSet.getDate("createTime");
saying.setSaying_id(sayingId);
saying.setAuthor(author);
saying.setSayingContent(sayingContent);
saying.setLikes(likes);
saying.setCreateTime(createTime);
System.out.println(saying);
}
// close sequence 相反跟打开的顺序
resultSet.close();
statement.close();
connection.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
// print result
Saying{
saying_id=1, author='huanghedidi', sayingAvatar='null', sayingContent='我喜欢,父亲的散文诗', likes='1,2,3,4,8', createTime=2019-05-06}
Saying{
saying_id=2, author='huanghedidi', sayingAvatar='null', sayingContent='second_test', likes='11', createTime=2019-05-03}
思考:当项目复杂庞大时候 直接使用原生的API会带那些问题?
1. 大量的重复性代码,SQL和业务代码耦合度极高, 维护难度很大,不利于项目迭代
2. 加大了出错概率 结果集需要手动处理 加大了开发难度
3. 需要手动管理JDBC资源
2. Spring JDBC
在spring里面对JDBC的封装 是怎么连接数据库?
我们先大体看一下spring的板块图,可见在spring的Data Access/Integration模块包括了对JDBC ORM等进行封装,如果要是用spring封装好的JDBCTemplate,需要引入对应的maven依赖:
<!-- 引入spring JDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
思考:引入spring的JDBC之后 我们应该怎么样的访问数据库呢?
其实spring对于Redis MongoDB RabbitMQ等 都提供了对应的template进行封装 简化开发人员操作
1. JDBCTemplate 对于数据库的连接管理 资源管理
2.1 spring JDBC对于结果集的处理
1.实现RowMapper接口,mapRow()方法
public class SayingRowMapper implements RowMapper {
@Override
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
Saying saying = new Saying();
saying.setSaying_id(resultSet.getInt("saying_id"));
saying.setAuthor(resultSet.getString("author"));
saying.setSayingAvatar(resultSet.getString("sayingAvatar"));
saying.setSayingContent(resultSet.getString("sayingContent"));
saying.setLikes(resultSet.getString("likes"));
saying.setCreateTime(resultSet.getDate("createTime"));
// 因为返回类型是Object 所以即使有多组结果的时候自动进行封装成list 或者 map
return saying;
}
}
2.转换结果集返回Object
@Autowired
private JdbcTemplate jdbcTemplate;
private List<Saying> sayings;
@Test
void MyRowMapperTest(){
sayings = jdbcTemplate.query("select * from saying where 1=1", new SayingRowMapper());
sayings.forEach(System.out::println);
}
思考: 这样写好像并没有简单多少啊?还是要手动进行类型转换 对象赋值
1. 需要做数据库字段名称和JAVA对象的字段的名称转换(下划线到驼峰命名)
2. 数据库字段类型转换成JAVA的引用类型或基本类型(tinyInt, int, bigint… varchar, char, text, datetime…)
3. 有没有一个自动的映射方法,解决以上的问题?
public class BaseRowMapper<T> implements RowMapper<T> {
private Class<?> targetClazz;
private HashMap<String, Field> fieldMap;
public BaseRowMapper(Class<?> targetClazz) {
this.targetClazz = targetClazz;
fieldMap = new HashMap<>();
Field[] fields = targetClazz.getDeclaredFields();
for (Field field : fields) {
fieldMap.put(field.getName(), field);
}
}
@Override
public T mapRow(ResultSet rs, int arg1) throws SQLException {
T obj = null;
try {
obj = (T) targetClazz.newInstance();
final ResultSetMetaData metaData = rs.getMetaData();
int columnLength = metaData.getColumnCount();
String columnName = null;
for (int i = 1; i <= columnLength; i++) {
columnName = metaData.getColumnName(i);
Class fieldClazz = fieldMap.get(camel(columnName)).getType();
Field field = fieldMap.get(camel(columnName));
field.setAccessible(true);
// fieldClazz == Character.class || fieldClazz == char.class
if (fieldClazz == int.class || fieldClazz == Integer.class) {
// int
field.set(obj, rs.getInt(columnName));
} else if (fieldClazz == boolean.class || fieldClazz == Boolean.class) {
// boolean
field.set(obj, rs.getBoolean(columnName));
} else if (fieldClazz == String.class) {
// string
field.set(obj, rs.getString(columnName));
} else if (fieldClazz == float.class) {
// float
field.set(obj, rs.getFloat(columnName));
} else if (fieldClazz == double.class || fieldClazz == Double.class) {
// double
field.set(obj, rs.getDouble(columnName));
} else if (fieldClazz == BigDecimal.class) {
// bigdecimal
field.set(obj, rs.getBigDecimal(columnName));
} else if (fieldClazz == short.class || fieldClazz == Short.class) {
// short
field.set(obj, rs.getShort(columnName));
} else if (fieldClazz == Date.class) {
// date
field.set(obj, rs.getDate(columnName));
} else if (fieldClazz == Timestamp.class) {
// timestamp
field.set(obj, rs.getTimestamp(columnName));
} else if (fieldClazz == Long.class || fieldClazz == long.class) {
// long
field.set(obj, rs.getLong(columnName));
}
field.setAccessible(false);
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 下划线转驼峰
* @param str
* @return
*/
public static String camel(String str) {
Pattern pattern = Pattern.compile("_(\\w)");
Matcher matcher = pattern.matcher(str);
StringBuffer sb = new StringBuffer(str);
if(matcher.find()) {
sb = new StringBuffer();
matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
matcher.appendTail(sb);
}else {
return sb.toString();
}
return camel(sb.toString());
}
}
有了这个baseMapper之后 我们处理起来就更加方便了
@Test
void MyRowMapperTest(){
sayings = jdbcTemplate.query("select * from saying where 1=1", new BaseMapper(Saying.class));
sayings.forEach(System.out::println);
}
思考:这样还是有一些问题没有解决啊
1. SQL语句硬编码,跟业务代码混在一起
2. 参数只能按顺序传入,插入时候实体类和数据库字段顺序一致
3. 没有实现实体类到数据库插入对应记录的映射
4. 没有实现缓存等功能
3. 什么是ORM框架
为什么叫ORM? ORM框架解决了那些问题?
O: Object对象 R: relation关系型数据库 M: mapper 映射
Java和数据库记录的相互转化
之前Hibernate也是一个比较优秀的ORM框架,使用hibernateTemplate会自动生成对应crud的语句;
Hibernate存在的缺点:
1. 不能指定部分字段,很不灵活
2. 无法自定义SQL,优化困难
3. 不支持动态SQL
4. Mybatis基本特性
基本特性:
1. 使用连接池对连接进行管理
2. SQL和业务代码分离解耦,集中进行管理
3. 支持参数映射和动态SQL
4. 结果集映射
5. 缓存管理
6. 重复SQL的提取
7. 插件机制,例如分页插件等
应该如何选择ORM框架?
- 业务简单 推荐Hibernate
- 业务复杂 需要灵活SQL 推荐mybatis
- 对性能要求高 推荐直接使用原生的JDBC
- Spring JDBC 和ORM混用
- 混用ORM基本没见过。。。推荐手写ORM!