前言:
開發操作數據庫,對於SQL語句是手寫。覺得麻煩,有的簡單的也需要自己寫多次
已知操作數據庫簡便的有
一:通過generator的數據庫生成表,會生成對應的數據庫單表的操作。
二:通過jpa的方法操作數據庫
三:通過自己來寫
generator 遇到重構的情況就很難受,並且要避免多次執行 會覆蓋原本的Model、DAO和映射文件的文件夾(踩過的坑)。。
jpa相對和mybatis的優劣這個問題我表示沒權威發言權,網上很多,更偏向於mybatis,小項目爲了效率選擇jpa
開始:
1、創建項目
2、啓動類加註解 @MapperScan 掃描mapper層
3、建一個公共的mapper 裏面有單表的 增、刪、改、查的操作
這裏就一個添加方法,然後可能有人不理解在 mapper上爲啥沒有@Mapper註解
@Mapper 是 Mybatis 的註解,和 Spring 沒有關係,@Repository 是 Spring 的註解,用於聲明一個 Bean
@Mapper 一定要有,否則 Mybatis 找不到 mapper。
@Repository 可有可無,可以消去依賴注入的報錯信息。
@MapperScan 可以替代 @Mapper。
用了@MapperScan 可以消除依賴注入的紅線報錯也不用在加@Mapper,所以直接在啓動類加了@MapperScan
4、創建mapper.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" >
<mapper namespace="com.test.springboot.mapper.BaseMapper" >
<insert id="shareInsert" parameterType="string" useGeneratedKeys="true" keyProperty="testId" keyColumn="id">
${sql}
</insert>
</mapper>
5、建一個公共的service 同樣有 增、刪、改、查的接口,只是類上加上泛型
6、新增兩個自定義註解 PS:針對數據庫與實體類的表名或字段名不一樣使用
第一個:
import java.lang.annotation.*;
/** 定義字段的註解 */
@Retention(RetentionPolicy.RUNTIME)
/** 該註解只能用在成員變量上 */
@Target(ElementType.FIELD)
/** 將註解包含在Javadoc中 */
@Documented
public @interface Column {
//默認爲空
String name() default "";
}
第二個:
package com.test.springboot.sql;
import java.lang.annotation.*;
/** 定義字段的註解 */
@Retention(RetentionPolicy.RUNTIME)
/** 表名 */
@Target(ElementType.TYPE)
/** 將註解包含在Javadoc中 */
@Documented
public @interface Table {
//默認爲空
String name() default "";
}
註解分爲:
/*註解級別信息 RetentionPolicy.RUNTIME:VM運行期間保留註解,可以通過反射機制讀取註解信息 RetentionPolicy.SOURCE:註解將被編譯器丟棄 RetentionPolicy.CLASS:註解在class文件中可用,但會被VM丟棄*/
/*自定義註解的使用範圍 ElementType.METHOD:方法聲明 ElementType.TYPE:類、接口(包括註解類型)或enum聲明 ElementType.CONSTRUCTOR:構造器的聲明 ElementType.FIELD:域(成員)聲明(包括enum實例) ElementType.LOCAL_VARIABLE:局部變量聲明 ElementType.PACKAGE:包聲明 ElementType.PARAMETER:參數聲明*/
7、增加實現類impl
@Service
public class BaseServiceImpl<T,T2> implements BaseService<T,T2> {
@Autowired
BaseMapper testTableMapper;
@Override
public Long shareInsert(T t) {
//獲取表名
String entityName = "";
if ( null == t.getClass().getAnnotation(Table.class) ){
//如果沒加table自定義標籤,直接使用實體類類名做爲表名
entityName = t.getClass().getSimpleName();
}else{
//反之使用自定義標籤的名稱作爲表名
entityName = t.getClass().getAnnotation(Table.class).name();
}
//聲明SQL字符串並寫上固定的SQL
StringBuilder sql = new StringBuilder("insert into "+entityName+" ");
//獲得SQL
Map<String,String> sqlMap = getAllPropertiesForSql(t, entityName);
//拼接爲完整SQL
sql.append(sqlMap.get("field")).append(sqlMap.get("value"));
return testTableMapper.shareInsert(sql.toString());
}
private Map<String,String> getAllPropertiesForSql(Object t){
Map<String,String> map = new HashMap<>();
if(null == t) return map;
StringBuilder filedSql = new StringBuilder("(");
StringBuilder valueSql = new StringBuilder("value (");
//獲取所有屬性
Field[] fields = t.getClass().getDeclaredFields();
for (Field field : fields) {
// 判斷該成員變量上是不是存在Column類型的註解
if (!field.isAnnotationPresent(Column.class)) {
continue;
}
Column c = field.getAnnotation(Column.class);// 獲取實例
// 獲取元素值
String columnName = c.name();
// 如果未指定列名,默認列名使用成員變量名
if ("".equals(columnName.trim())) {
columnName = field.getName();
}
// 獲取對象方法名
String methodName = getMethodNameByField("get", field.getName());
// 獲得此方法的反射
Method method = null;
try {
method = t.getClass().getDeclaredMethod(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// 執行方法獲取返回值
Object value = null;
try {
value = method.invoke(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
filedSql.append(columnName + ",");
valueSql.append("'" + value + "',");
}
//最後一個逗號刪除掉
valueSql.deleteCharAt(valueSql.length() - 1);
filedSql.deleteCharAt(filedSql.length() - 1);
//加上結尾的反括號
valueSql.append(") ");
filedSql.append(") ");
//添加到返回map中
map.put("field",filedSql.toString());
map.put("value", valueSql.toString());
return map;
}
public static String getMethodNameByField(String type, String name) {
return type + name.substring(0, 1).toUpperCase()
+ name.substring(1, name.length());
}
8、創建公共的控制類(或者不創建,直接使用,我沒創建,測試直接用)
@RestController
public class testController {
@Autowired
BaseServiceImpl<testTable,Long> baseService;
@RequestMapping(value = "/test",method = RequestMethod.GET)
public void test(){
testTable testTable = new testTable();
testTable.setName("測試2");
testTable.setTestValue("1234567");
Long aLong = baseService.shareInsert(testTable);
}
}
添加返回的添加的id;
PS:這裏有注意的是,當遇到時間時,第七步裏面的設置value需要做一下轉換。
if ("Date".equals(field.getType().getSimpleName())) {
valueSb.append("'" + DateUtil.getDfStr4Date((Date) value) + "',");
} else {
valueSb.append("'" + value + "',");
}
這種有點問題,因爲是動態生成SQL,可能會有SQL注入。然後其他的刪,改,查都是這種套路。
簡單的操作套路瞭解了,其他的連表,以及查詢的函數等等,需要進一步瞭解以及編寫,暫時記錄一版簡單的避免忘掉。
對於這種個人不太喜歡,有SQL注入的風險,接收是中文,然後代碼拼接的SQL,且SQL直接使用的,沒有mybatis安全的#{}方式。
改變是:mapper傳遞一個實體和SQL,SQL生成的時候不採用取值的方式,而是拼接爲 insert into table(id,name,value) values(#{table.id},#{table.name},#{table.value});這種形式。
@Service
public class BaseServiceImpl<T,T2> implements BaseService<T,T2> {
@Autowired
BaseMapper testTableMapper;
@Override
public Long shareInsert(T t) {
//獲取表名
String entityName = "";
if ( null == t.getClass().getAnnotation(Table.class) ){
//如果沒加table自定義標籤,直接使用實體類類名做爲表名
entityName = t.getClass().getSimpleName();
}else{
//反之使用自定義標籤的名稱作爲表名
entityName = t.getClass().getAnnotation(Table.class).name();
}
//聲明SQL字符串並寫上固定的SQL
StringBuilder sql = new StringBuilder("insert into "+entityName+" ");
//獲得SQL
Map<String,String> sqlMap = getAllPropertiesForSql(t, entityName);
//拼接爲完整SQL
sql.append(sqlMap.get("field")).append(sqlMap.get("value"));
return testTableMapper.shareInsert(sql.toString());
}
private Map<String,String> getAllPropertiesForSql(Object t, String objName){
Map<String,String> map = new HashMap<>();
if(null == t) return map;
StringBuilder filedSql = new StringBuilder("(");
StringBuilder valueSql = new StringBuilder("value (");
//獲取所有屬性
Field[] fields = t.getClass().getDeclaredFields();
for (Field field : fields) {
// 判斷該成員變量上是不是存在Column類型的註解
if (!field.isAnnotationPresent(Column.class)) {
continue;
}
Column c = field.getAnnotation(Column.class);// 獲取實例
// 獲取元素值
String columnName = c.name();
// 如果未指定列名,默認列名使用成員變量名
if ("".equals(columnName.trim())) {
columnName = field.getName();
}
filedSql.append(columnName + ",");
//objName 表名 field.getName()字段名 --這裏的表名因爲多表,而要作爲公共的,所以這裏需要調整下,可以不傳表名而採用泛型
valueSql.append("#{" + objName + "." + field.getName() + "},");
}
//最後一個逗號刪除掉
valueSql.deleteCharAt(valueSql.length() - 1);
filedSql.deleteCharAt(filedSql.length() - 1);
//加上結尾的反括號
valueSql.append(") ");
filedSql.append(") ");
//添加到返回map中
map.put("field",filedSql.toString());
map.put("value", valueSql.toString());
return map;
}
得到的結果是:
insert into testTable (testId,testName,testValue) value (#{testTable.id},#{testTable.name},#{testTable.testValue})
這種格式在xml裏面就安全得多。PS:因測試所以直接寫死,作爲共用的需要採用泛型
記錄到此完結