手寫MyBatis
1. 前言
本篇博客,將使用JDK動態代理、注解、反射等技術,編寫一個最簡單的MyBatis,可基本實現對象的增刪查改
2. 注解的定義
2.1 Delete注解
/**
* @ClassName Delete
* @Descriiption 刪除注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Delete {
public String value();
}
2.2 Insert注解
/**
* @ClassName Delete
* @Descriiption 保存注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Insert {
public String value();
}
2.3 Select注解
/**
* @ClassName Delete
* @Descriiption 查詢注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
public String value();
}
2.4 Update注解
/**
* @ClassName Delete
* @Descriiption 更新注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Update {
public String value();
}
3. jdk動態代理
3.1 方法代理類
/**
* @ClassName MethodProxy
* @Descriiption 方法代理
* @Author yanjiantao
* @Date 2019/6/27 11:11
**/
public class MethodProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return DaoOperatorHandler.handle(method,args);
}
}
該類實現JDK的InvocationHandler方法,并且實驗invoke方法,即可實現JDK的動態代理
3.2 動態代理工廠類
/**
* @ClassName MethodProxyFactory
* @Descriiption 代理工廠類
* @Author yanjiantao
* @Date 2019/6/28 15:40
**/
public class MethodProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
final MethodProxy methodProxy = new MethodProxy();
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{clazz},
methodProxy
);
}
}
該工廠的方法主要是得到Mapper的實例,并且把Mapper交給JDK進行動態代理
4. 數據庫操作
4.1 數據庫操作處理類
/**
* @ClassName DaoOperatorHandler
* @Descriiption 數據庫操作處理器
* @Author yanjiantao
* @Date 2019/6/27 11:39
**/
public class DaoOperatorHandler {
public static Object handle(Method method, Object[] parameters) throws SQLException, ClassNotFoundException {
String sql = null;
// 插入
if (method.isAnnotationPresent(Insert.class)) {
sql = checkSql(method.getAnnotation(Insert.class).value(), Insert.class.getSimpleName());
insert(sql, parameters);
// 更新
}else if (method.isAnnotationPresent(Update.class)) {
sql = checkSql(method.getAnnotation(Update.class).value(), Update.class.getSimpleName());
return update(sql, parameters);
// 查詢
}else if (method.isAnnotationPresent(Select.class)) {
sql = checkSql(method.getAnnotation(Select.class).value(), Select.class.getSimpleName());
Class returnType = method.getReturnType();
if (List.class.isAssignableFrom(returnType)) {
return selectMany(sql, parameters);
}else {
return selectMany(sql, parameters).get(0);
}
}else if (method.isAnnotationPresent(Delete.class)) {
sql = checkSql(method.getAnnotation(Delete.class).value(), Delete.class.getSimpleName());
return update(sql, parameters);
}
System.out.println(sql);
return null;
}
/**
* 插入
* @param sql sql
* @param parameters 參數
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static void insert(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
statement.setObject(i+1, (String) parameters[i]);
}
statement.execute();
connection.close();
}
/**
* 插入
* @param sql sql
* @param parameters 參數
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static Integer update(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
statement.setObject(i+1, parameters[i]);
}
int result = statement.executeUpdate();
connection.close();
return result;
}
/**
* 插入
* @param sql sql
* @param parameters 參數
* @return List<T>
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static <T> List<T> selectMany(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; parameters != null && i < parameters.length; i++) {
statement.setObject(i+1, parameters[i]);
}
ResultSet resultSet = statement.executeQuery();
List<T> result = new ResultToMapper<T>().mapToObject(resultSet,User.class);
return result;
}
/**
* 檢查sql
* @param sql sql
* @param type type
* @return the sql
* @throws SQLException SQLException
*/
private static String checkSql(String sql, String type) throws SQLException {
String sqlType = sql.split(" ")[0];
if (!sqlType.equalsIgnoreCase(type)) {
throw new SQLException("SQL語句錯誤");
}
return sql;
}
}
該類主要是根據被代理類是否包含相關注解,根據注解的類型,進行增刪查改的操作,最后,再將增刪查改后的處理結果,使用反射映射到實體類上
5 實體類
5.1用戶實體類
/**
* @ClassName User
* @Descriiption 用戶實體類
* @Author yanjiantao
* @Date 2019/6/28 15:24
**/
@Data
public class User {
private Integer id;
private String username;
private String password;
}
6 工具類
6.1 JDBCUtils
/**
* @ClassName JDBCUtils
* @Descriiption jdbc連接工具類
* @Author yanjiantao
* @Date 2019/6/28 16:24
**/
public class JDBCUtils {
public static Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String username = "root";
String password = "root123456";
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8", username, password);
}
}
6.2 ResultToMapper
/**
* @ClassName ResultToMapper
* @Descriiption mysql查詢結果轉換為實體bean
* @Author yanjiantao
* @Date 2019/6/28 17:35
**/
public class ResultToMapper<T> {
public List<T> mapToObject(ResultSet resultSet, Class<?> clazz) {
if (resultSet == null) {
return null;
}
List<T> result = null;
try {
while (resultSet.next()) {
T bean = (T) clazz.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 0; i < metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i + 1);
Object columnValue = resultSet.getObject(i + 1);
Field field = clazz.getDeclaredField(columnName);
if (field != null && columnValue != null) {
field.setAccessible(true);
field.set(bean,columnValue);
}
}
if (result == null) {
result = new ArrayList<>();
}
result.add(bean);
}
} catch (SQLException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
if (result == null) {
return Collections.emptyList();
}
return result;
}
}
該類主要是將mysql查詢的結果,通過反射,映射到實體類上
7 Mapper
public interface UserMapper {
@Insert("insert into user (username,password) values (?,?)")
public void addUser(String name, String password);
@Select("select * from user")
public List<User> findUsers();
@Select("select * from user where id = ?")
public User getUser(Integer id);
@Update("update user set username = ? , password=? where id=?")
public Integer updateUser(String name, String password, Integer id);
@Delete("delete from user where id=?")
public Integer deleteUser(Integer id);
}
8 測試類
@Slf4j
public class UserMapperTest {
@Test
public void addUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
userMapper.addUser("boolean-","123456");
log.info("---------->");
}
@Test
public void findUsers() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
List<User> list = userMapper.findUsers();
log.info("---------->list={}", list);
}
@Test
public void getUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
User user = userMapper.getUser(2);
log.info("---------->user={}", user);
}
@Test
public void updateUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
Integer result = userMapper.updateUser("鄢劍濤update", "yjt123", 1);
log.info("count={}", result);
}
@Test
public void deleteUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
Integer count = userMapper.deleteUser(1);
log.info("count={}", count);
}
}
9 總結
這次的編寫簡單的mybatis,讓我對java基礎有了進一步的了解,明白了反射、注解的厲害之處,也了解了JDK動態代理設計模式,總之,收獲很大!!

浙公網安備 33010602011771號