代码来源于高淇JAVA教学视频 谢谢高淇老师的教学。

因为自己在学习的过程中发现了很多困难点,总结下希望对自己接下来学框架提升。给像我一样得初学者方便。

SORM框架是一个简单的ORM,关系对象映射,可以通过这个框架方便的更改和操作一些数据库的东西,在框架运行的时候也会根据数据库中的表生成相应的Javabean在po包下。通过直接对po包下的操作在运用query中的一些操作就可以实现对数据库的操作。思路会在代码中注释。

QueryFactory直接生产出Query给用的人调用,Query作为一个接口是更好的让多种数据库提供各自的操作。实现mySQLQuery或者OracleQuery,conventor是个转换不同语言数据类型的工具

TableContext就是核心的程序连接数据库表的类。DBManager钟放的是一些配置资源文件和一些加载处理。

明确主要思路,就是通过factory制作一个query,然后直接set方法然后将对象传入query的方法实现功能。查询的话也是通过queryrows方法传入select语句进行查询,因为查询语句的多样性简单的框架没有进一步封装。

先从最底层的tablecontext和DBManage说起:

解释会在代码中标识

public class DBManager {
private static Configuration conf;               //这是在bean包下定义的一个类用来把从配置文件提取出来方便用get set
private static List<Connection> pool=new ArrayList<>();      //这是连接池的定义,我在老师的基础上做了些更改,我把连接池全部放到了这个类里 static{                                      //静态代码块初始化资源文件中的数据, 注意静态块是和类一起加载的,大量用静态影响内存
Properties pro=new Properties();                      //这个类是用来从资源文件中提取数据的类
try {
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("database.properties"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conf=new Configuration();
conf.setDriver(pro.getProperty("driver"));
conf.setPoPackage(pro.getProperty("poPackage"));
conf.setPwd(pro.getProperty("pwd"));
conf.setScrPath(pro.getProperty("srcPath"));
conf.setUser(pro.getProperty("user"));
conf.setUrl(pro.getProperty("url"));
conf.setUsingDB(pro.getProperty("usingDB"));
conf.setQueryClass(pro.getProperty("queryClass"));
conf.setMAX_POOL(Integer.parseInt(pro.getProperty("max_pool")));
conf.setMIN_POOL(Integer.parseInt(pro.getProperty("min_pool")));
System.out.println(TableContext.class);//加载类                          //这是用来加载表信息的,用于表连接也可以用反射来加载,但是要trycatch选择输出加载
initPool();                                              //加载连接池
} public static void initPool(){                          //连接池的初始化
if(pool==null){
pool=new ArrayList<Connection>();
} while(pool.size()<conf.getMIN_POOL()){
pool.add(DBManager.createConn());
//初始化
}
} public static Connection getConn(){ //连接池取连接
int last_index=pool.size()-1;
Connection conn=pool.get(last_index);
pool.remove(last_index);
return conn;
} public static void CloseConn(Connection conn){                          //连接池关闭
if(pool.size()>conf.getMAX_POOL()){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
pool.add(conn);
}
} public static Connection createConn(){
try {                                                //真正的建立一个连接
Class.forName(conf.getDriver());
return DriverManager.getConnection(conf.getUrl(),conf.getUser(),conf.getPwd());
//直接建立连接
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} }
                                                     //get用于配置信息的哪个类,很好理解
public static Configuration getConf(){
return conf;
}
                                                    //之后的关闭就不说了,主要是要记得把关闭改变成从线程池中假关闭
public static void close(ResultSet rs,Statement ps,Connection conn){
try {
if(rs!=null){
rs.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(ps!=null){
ps.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(conn!=null){
CloseConn(conn);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } public static void close(Statement ps,Connection conn){
try {
if(ps!=null){
ps.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(conn!=null){
CloseConn(conn);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

然后现在有配置文件和连接了

接下来是表连接tablecontect

/**
*
* 管理数据库所有表结构和类结构的关系,并可以根据表结构生成类结构
* @author NodeRed
*
*/
public class TableContext { //最核心的 可以根据表生成类。
/**
* 表名为key,信息对对象为value
*/ public static Map<String,TableInfo> tables=new HashMap<>(); //这个表中的tableInfo也是定义的一个类大概意思就是能够记录下整张表,这里这个map就能记录整个database /**
* 将po的class对象和表关联
*/
public static Map<Class, TableInfo> poClassTableMap=new HashMap<>(); //这个表用来记录这个表后来会产生哪个类,这后面会说 private TableContext(){};
//无参构造很重要 static{
try {
Connection con=DBManager.getConn(); //这里就获得了和数据库的连接了
DatabaseMetaData dbmd=con.getMetaData(); //这里原视频中没有说,大概是 可以从连接中搜索获取一些元数据,%是类似所有
ResultSet tableRet=dbmd.getTables(null, "%", "%",new String[]{"TABLE"}); //TABLE那就是要查询的就是表了,但是这里出现了问题,意外的搜索出了创建表的时间
System.out.println(tableRet); //记录的那个表,捯饬了半天也没解决。 然后之后是放到了一个结果集里
while(tableRet.next()){ //遍历查找出来的结果
String tableName=(String)tableRet.getObject("TABLE_NAME"); //获得表名
TableInfo ti=new TableInfo(tableName, new ArrayList<ColumnInFo>(), // 然后把表名先把上说的可以记录下整个表的new出来,具体的后面再说
new HashMap<String,ColumnInFo>());
tables.put(tableName, ti); //放到记录整个数据库的那个表里
ResultSet set=dbmd.getColumns(null, "%", tableName ,"%"); //这里根据表名获取字段集,
while(set.next()){
ColumnInFo ci=new ColumnInFo(set.getString("COLUMN_NAME"),set.getString("TYPE_NAME"),0);//可以看出这里获取了字段的名字和类型
ti.getColumns().put(set.getString("COLUMN_NAME"),ci); //这里是放到表映射,加载表的字段
} ResultSet set2=dbmd.getPrimaryKeys(null, "%", tableName);
while(set2.next()){   //这里加载主键
ColumnInFo ci2=(ColumnInFo)ti.getColumns().get(set2.getObject("COLUMN_NAME"));
ci2.setKeyType(1);
ti.getPriKeys().add(ci2);
}
if(ti.getPriKeys().size()>0){
ti.setOnlyPrivate(ti.getPriKeys().get(0));
} }                                  //如果这里没有懂得话,不要急,后面结合javabean //这里就可以理解为,你在java中建立了一个表格,不是完全填好数据得表格,只是单纯得有每个数据类型在哪个表是不是主键得表示
有了这个标识以后可以在query中找出数据库要操作得是哪个,再从query中操作数据库。
} catch (Exception e) {
// TODO: handle exception
}
updataJavaPOFile();                        //因为我们要再数据库中操作,程序事先是不知道你表中得每一项叫什么名字就没办法根据数据可定义好类,在用类来操作数据库                        
loadPOTables(); //这里我们就可以根据数据库中获取的表框架,获取表的名字,表中字段类型,生成这个数据库的java类
//然后是每次加载这个类的时候更新
}  
//一下是实现方法
public static void updataJavaPOFile(){
Map<String,TableInfo> map= TableContext.tables; 这里就通过这个tables表然后用后面的一个java类实现了在项目中构造java类
// TableInfo t=map.get("new_table");
for(TableInfo t:map.values()){
JavaFileUtils.createJavaPOFile(t, new MysqlTypeConventor()); }
} public static void loadPOTables(){
// Class c=Class.forName("com.cy.sorm.New_table");
// poClassTableMap.put(c, TableInfo); //这里就是用反射把这个类和产生自哪个表放在了一个表里,在后面的操作有用
for(TableInfo tableInfo:tables.values()){
try{
Class c=Class.forName(DBManager.getConf().getPoPackage()+
"."+StringUTils.firstChar2UpCase(tableInfo.getTname()));
poClassTableMap.put(c, tableInfo);
}catch(Exception e){
e.printStackTrace(); }
} } // public static void main(String[] args) {
// Map<String, TableInfo> tables=TableContext.tables;
// System.out.println(tables);
// } }

 这里也是orm的核心了,从数据库中复制了一个映射表过来。但是老师说在工作中用的很少。接下来说一下字段映射和表映射吧,可以从以上的javabean包中可以看出,除了很容易理解的配置configuration,javasetget也就是在上面说的拼接我们自己产生的java类的简单结构。

    private String name;

    private String Datatype;

    private int keyType;

字段就是在mysql建表时候的每一列

    private String tname;
/**
* key 字段名 value字段类
*/
private Map<String,ColumnInFo> columns; private ColumnInFo onlyPrivate; private List<ColumnInFo> priKeys;

这个表呢也就可以记录下整个表了 map中也就是整个表,记录下主键 很好理解这样我们只要有足够多的数据传入我们就可以在java中改了整张表,但是我们必去得做一个query类,用来我把这个数据类传入就可以在数据库中同步更改。

然后大概思路也就成型了,用户可以提供数据库 ,我自动给他生成他表中得类,他调用query中方法,对数据库进行更改。但是怎么通过我生成类对这个映射相联系,然后再映射到数据库呢,我们藏了一个map。

先说一点简单得把tables中获得得数据进行生成java类

这里实现了一个接口,可以处理不同数据库到java得翻译数据类型这里很好懂

public class MysqlTypeConventor implements TypeConvertor{

    @Override
public String databaseType2JavaType(String columnType) {
if("varchar".equalsIgnoreCase(columnType)||"char".equalsIgnoreCase(columnType)){
return "String";
}else if("int".equalsIgnoreCase(columnType)
||"tinyint".equalsIgnoreCase(columnType)){
return "Integer";
}else if("double".equalsIgnoreCase(columnType)){
return "double"; }else if("timestamp".equalsIgnoreCase(columnType)){
return "Timestamp";
}
return null;
} @Override
public String javaType2databaseType(String javaDataType) {
// TODO Auto-generated method stub
return null;
} }

然后我们把转换器和传进去的字段封装成一个java生成类的工具,就是上面代码提到的哪个工具类

  

public class JavaFileUtils {

    public static JavaFieldGetSet createFieldGetSetSRC(ColumnInFo column,TypeConvertor convertor){

        JavaFieldGetSet jfgs= new JavaFieldGetSet();
String javaFieldType = convertor.databaseType2JavaType(column.getDatatype());
jfgs.setFieldInfo("\tprivate "+javaFieldType+" "+column.getName());
StringBuilder getSrc=new StringBuilder();
getSrc.append("\tpublic "+javaFieldType+" get"+StringUTils.firstChar2UpCase(column.getName())+"(){\n");
getSrc.append("\t\treturn "+column.getName()+";\n");
getSrc.append("\t}\n");
jfgs.setGetInfo(getSrc.toString()); StringBuilder setSrc=new StringBuilder();
setSrc.append("\tpublic void set"+StringUTils.firstChar2UpCase(column.getName())+"(");
setSrc.append(javaFieldType+" "+column.getName()+"){\n");
setSrc.append("\t\tthis."+column.getName()+"="+column.getName()+";\n");
setSrc.append("\t}\n");
jfgs.setSetInfo(setSrc.toString()); return jfgs;
} public static String createJavaSrc(TableInfo tableInfo,TypeConvertor convertor){ Map<String,ColumnInFo> columns=tableInfo.getColumns();//表装入map
List<JavaFieldGetSet> javaFields=new ArrayList<>(); for(ColumnInFo c:columns.values()){
javaFields.add(createFieldGetSetSRC(c, convertor));
}
StringBuilder src=new StringBuilder();
src.append("package "+DBManager.getConf().getPoPackage()+";\n\n");
src.append("import java.util.*;\n\n");
src.append("import java.sql.*;\n\n");
src.append("public class "+StringUTils.firstChar2UpCase(tableInfo.getTname())+"{\n\n"); for(JavaFieldGetSet f:javaFields){
src.append(f.getFieldInfo()+";\n");
}
src.append("\n\n");
for(JavaFieldGetSet f:javaFields){
src.append(f.getSetInfo());
}
src.append("\n");
for(JavaFieldGetSet f:javaFields){
src.append(f.getGetInfo());
}
src.append("}\n"); // System.out.println(src);
return src.toString(); } public static void createJavaPOFile(TableInfo tableInfo,TypeConvertor convertor){
String src=createJavaSrc(tableInfo, convertor); BufferedWriter bw=null;
String srcPath=DBManager.getConf().getScrPath()+"\\";
String packagePath=DBManager.getConf().getPoPackage().replaceAll("\\.", "\\\\");
File f=new File(srcPath+packagePath.trim());
System.out.println(f); if(!f.exists()){
f.mkdirs();
} try {
bw=new BufferedWriter(new FileWriter(f.getAbsoluteFile()+"\\"+StringUTils.firstChar2UpCase(tableInfo.getTname())+".java"));
bw.write(src);
} catch (Exception e) {
// TODO: handle exception
}finally {
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} } public static void main(String[] args) {
// ColumnInFo ci=new ColumnInFo("username","varchar",0);
// JavaFieldGetSet f=createFieldGetSetSRC(ci, new MysqlTypeConventor());
// System.out.println(f);
Map<String,TableInfo> map= TableContext.tables;
// TableInfo t=map.get("new_table");
for(TableInfo t:map.values()){
JavaFileUtils.createJavaPOFile(t, new MysqlTypeConventor()); } }
}

第一个方法就是把从字段类中获取的名字类型转换成一个String,这里没有表名所以拼接了的是getset,便于拼接是用stringbuilder拼接,

第二个方法有了表映射以后我们就可以拼接表名,然后从表映射中获取有那些字段,再拼接上字段的setget方法,最后呢就是通过配置文件中获得我们项目路径,然后建立java类

这里注意把“ .  ”换成"  \\ "的操作java中一个变两个

然后就是核心的实现query了,这里本来是想实现接口 然后方便不同的数据库连接不同的,然后后来因为方法重用的很多就改成了抽象类。

public abstract class Query {

    public List executeQueryTemplate(String sql, Object[] params,Class clazz,CallBack back){
Connection conn=DBManager.getConn();
PreparedStatement ps=null;
ResultSet rs=null;
try {
ps=conn.prepareStatement(sql);
JDBCUtils.handleParams(ps, params); //这个是一个回调模板,通过callback,callback是一个简单的接口,很常用,实现的作用就是,我实现一个接口,接口中是我要实现的功能
rs=ps.executeQuery(); //然后我可以接着写下去,这成为了一个模板,只是中间这个方法的实现不一样,当我要使用这个模板的时候调用这个方法,然后匿名内部类实现back
return back.doExecute(conn, ps, rs); //中的方法但是匿名内部类又涉及到用外部变量 final的问题,我用过的也很少希望在之后的学习有更深的理解。
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}finally {
DBManager.close(rs,ps,conn);
}
} public int excuteDML(String sql,Object[] params){
Connection conn=DBManager.getConn();
int count=0; //这是个执行sql语句的封装,object参数是传入要操作的数因为用的是preparestatement
PreparedStatement ps=null;
try {
ps=conn.prepareStatement(sql);
JDBCUtils.handleParams(ps, params);
count=ps.executeUpdate(); } catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
DBManager.close(ps,conn);
} return count;
} public void insert(Object obj){
Class c=obj.getClass();
List<Object> params=new ArrayList<>();//储存sql的参数对象
int countNotNull=0;//计算不为空的属性值
TableInfo tableInfo = TableContext.poClassTableMap.get(c);//////////////////这就是map记录了class和映射table的关系,然后返回映射表就可以获得表名拼接sql语句了
StringBuilder sql=new StringBuilder("insert into "+tableInfo.getTname()+" (");
Field[] fs=c.getDeclaredFields(); //利用反射将对象的的名字和操作值然后拼接字符串
for(Field f:fs){
String fieldName=f.getName();
Object fieldValue=ReflectUtils.invokeGet(fieldName, obj); //这里是利用反射 获取get方法 get到我们设置的值 然后拼接到sql语句中,
                                                  //这样一来最开始的思路也解决了 if(fieldValue!=null){
countNotNull++;
sql.append(fieldName+",");
params.add(fieldValue); }
}
sql.setCharAt(sql.length()-1, ')');
sql.append(" value (");
for(int i=0;i<countNotNull;i++){
sql.append("?,");
}
sql.setCharAt(sql.length()-1, ')');
excuteDML(sql.toString(), params.toArray());
} public void delete(Class clazz,Object id){
/**
* table.class->delete from New_table where id=2;
*/
TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); ColumnInFo onlyPriKey=tableInfo.getOnlyPrivate(); //mysql删除宇语句中我们只要获取表主键的编号就可以删除了 String sql="delete from "+tableInfo.getTname()+" where "+onlyPriKey.getName()+"=? "; excuteDML(sql, new Object[]{id});
} public void delete(Object obj){
Class c=obj.getClass();
TableInfo tableInfo=TableContext.poClassTableMap.get(c); //同理反射获得get方法 然后用第一个delete方法删除
ColumnInFo onlyPriKey= tableInfo.getOnlyPrivate();
Object priKeyValue=ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
delete(c,priKeyValue);
} public int update(Object obj,String[] fieldNames){
//obj("uname",pwd)--->update 表名 set uname=?,pwd=? where id =?
Class c=obj.getClass();
List<Object> params=new ArrayList<>();
TableInfo tableInfo=TableContext.poClassTableMap.get(c);
ColumnInFo priKey =tableInfo.getOnlyPrivate();
StringBuilder sql=new StringBuilder("update "+tableInfo.getTname()+" set ");
for(String fname:fieldNames){
Object fvalue=ReflectUtils.invokeGet(fname, obj);
params.add(fvalue);
sql.append(fname+"=?,");
//和insert很像加了遍历要更新的列 然后list记录问好处要更新的值 反射获取值
}
sql.setCharAt(sql.length()-1, ' ');
sql.append(" where ");
sql.append(priKey.getName()+"=? ");
params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
return excuteDML(sql.toString(), params.toArray());
} //返回多行
public List queryRows(String sql, Class clazz, Object[] params){
return executeQueryTemplate(sql,params,clazz,new CallBack(){ ///接上回调方法的实现 public List doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
List list=null;
try {
ResultSetMetaData metaData=rs.getMetaData();
while(rs.next()){
if(list==null){
list=new ArrayList();
}
Object rowObj=clazz.newInstance();//调用javabeen的无参构造器 // select username,pwd,age from user where id>? and age<18
for(int i=0;i<metaData.getColumnCount();i++){ //getcolumnCount为查询要差的列如 username pwd age String columnName=metaData.getColumnLabel(i+1);
Object columnValue=rs.getObject(i+1);
ReflectUtils.invokeSet(rowObj, columnName, columnValue); }
list.add(rowObj);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return list;
}
}); } //返回一行
public Object queryUniqueRows(String sql, Class clazz,Object[] params){
List list=queryRows(sql, clazz, params); return (list==null&&list.size()>0)? null:list.get(0);
} public Object queryValue(String sql,Object[] params){
Connection conn=DBManager.getConn();
Object value=null;
PreparedStatement ps=null;
ResultSet rs=null; try {
ps=conn.prepareStatement(sql);
JDBCUtils.handleParams(ps, params);
rs=ps.executeQuery();
// rs中为查到的行数
while(rs.next()){
value=rs.getObject(1); //rs结果集中不是以0开头的
} } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return value;
} public Number queryNumber(String sql,Object[] params){
return (Number)queryValue(sql, params);
} public abstract Object queryPagenate(int PageNum,int size); //抽象方法,不同子类不同实现
}

之后就可以继承query实现不同的数据库的不同方法然后重写和修改实现多态

然后实现了query后就可以工厂设计模式,用factory单例,然后生产query

public class QueryFactory {

    private static QueryFactory factory=new QueryFactory();
private static Class c; static{
try {
c=Class.forName(DBManager.getConf().getQueryClass());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} private QueryFactory() {
// TODO Auto-generated constructor stub
} public Query creatFactory(){
try {
return (Query) c.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}

关于高淇JAVA中SORM总结学习笔记详细个人解释的更多相关文章

  1. Java中的权限学习笔记

    1.Java中的权限有两个层次,一个是类这一层,另一个是类成员那一层. 类这一层: public class可以在本包内被访问,也可以在包外被访问.而没有被public修饰的class只能在本包内被调 ...

  2. java学习——java中的反射学习笔记

    Java--reflect 一.Class类的使用 什么是Class类? 1:在面向对象的世界中,万事万物皆对象. java语言中,静态的成员,普通数据类型类是不是对象呢? 是,对象!是类的对象! 类 ...

  3. 《Java并发编程的艺术》第5章 Java中的锁 ——学习笔记

    参考https://www.cnblogs.com/lilinzhiyu/p/8125195.html 5.1 Lock接口 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同 ...

  4. Java中的包学习笔记

    一.总结 1.引入包的概念的原因和包的作用比如有多个人开发一个大型程序,A定义了一个Math.java类,B也定义了一个Math.java类,它们放在不同目录,使用的时候也是用目录来区分,包实际上就是 ...

  5. java中String类学习笔记

    1.String的两种实例化方式 String str="hello";//直接赋值的方式: String str=new String("hello");// ...

  6. java中JVM虚拟机内存模型详细说明

    java中JVM虚拟机内存模型详细说明 2012-12-12 18:36:03|  分类: JAVA |  标签:java  jvm  堆内存  虚拟机  |举报|字号 订阅     JVM的内部结构 ...

  7. Java中的三大特性 - 超详细篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的三大特性 - 超详细篇>,希望对大家有帮助,谢谢 这一节的内容可能有点多,大家可以选择性的来看 简介 Java的三大特性:封装.继 ...

  8. Java后端高频知识点学习笔记1---Java基础

    Java后端高频知识点学习笔记1---Java基础 参考地址:牛_客_网 https://www.nowcoder.com/discuss/819297 1.重载和重写的区别 重载:同一类中多个同名方 ...

  9. JAVA的反射机制学习笔记(二)

    上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了.自己的步伐全然被打乱了~不能继续被动下去.得又一次找到自己的节奏. 4.获取类的Constructor 通过反射机制得到 ...

随机推荐

  1. xampp修改mysql 启动脚本

    打开xmapp,点击mysql对应的config按钮进入my.ini文件,如图所示: 修改mysqld服务的port参数3306为你想要设置的port,如图2所示: 重新启动mysql服务即可用客户端 ...

  2. 【SQL server基础】判断数据库、表格、视图、存储过程、函数书否存在

    库是否存在 if exists(select * from master..sysdatabases where name=N'库名') print 'exists' else print 'not ...

  3. Redis分布式锁的一点小理解

    1.在分布式系统中,我们使用锁机制只能保证同一个JVM中一次只有一个线程访问,但是在分布式的系统中锁就不起作用了,这时候就要用到分布式锁(有多种,这里指 redis) 2.在 redis当中可以使用命 ...

  4. asp.net core 3.0 更新简记

    asp.net core 3.0 更新简记 asp.net core 3.0 更新简记 Intro 最近把活动室预约项目从 asp.net core 2.2 更新到了 asp.net core 3.0 ...

  5. Ionic2优于Ionic1的6个理由

    经历了一个从0到有的app的开发,我已经很熟悉Ionic1了,在此期间我曾发现过Ionic1的一些bug,和一些不合理的地方(根基版本 不同,后续我会陆续发表这些文章),我甚至在此期间对Ionic1进 ...

  6. linux登录后出现-bash-4.1$

    linux登录后有时候会出现-bash-4.1$ 造成这样的原因: 与这个用户有关环境变量没了,有关的文件被删除.也就是用户的家目录下面 .bash_profile .bashrc 被删除. 解决办法 ...

  7. 网络下载器 Internet Download Manager v6.35.5 绿色便携版

    Internet Download Manager,简称 IDM,是国外的一款优秀网络下载工具.目前凭借着下载计算的速度优势在外媒网站中均受好评,现在已被多数国人熟知.Internet Downloa ...

  8. Docker 安装Oracle

    1.使用docker 命令搜索oracle 镜像,前提是已安装了Docker docker  search oracle 2.下载相应版本的oracle 镜像 docker pull sath89/o ...

  9. 树莓派3B/3B+ 清华镜像系统和安装中文输入法Fcitx及Google拼音输入法

    你还在为树莓派无法安装中文输入法而到处找教程吗? 你还在为树莓派每次下载都要远隔重洋获取资源,龟速下载而烦恼吗? 为了解决这个问题,在这篇树莓派教程中,我将手把手叫你怎样安装 清华镜像系统和中文输入法 ...

  10. Jenkins构建Jmeter项目之源代码管理(SVN)

    1.查看项目创建中是否又svn插件,没有的话下载插件subversion 2.配置svn源代码管理,如下图(testcases目录下包含build.xml和脚本文件) 3.查看Jenkins本地工作空 ...