热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

优雅的android数据库编程

android的数据库编程,说白了就是如何操控Sqlite,其实网上帖子一大把,大多都已经能够完成我们的数据开发任务。今天我要讲的是,如何把这件事情做的优雅点。。首先,就涉及到如何定义“优雅”。我想

android 的数据库编程,说白了就是如何操控Sqlite,其实网上帖子一大把,大多都已经能够完成我们的数据开发任务。

今天我要讲的是,如何把这件事情做的优雅点。。

首先,就涉及到如何定义“优雅”。我想对数据库的操作,优雅,就是你去定义个表格,比如播放记录(见谅,我是做视频的,相信大家都喜欢看我司的片,4亿观众总有你)。

当然,出了播放记录还有一些偏好/下载信息等等等等,这些,都可以存储在Sqlite里面。这些表,我们特别希望,就是在android的代码里面定义这些表,跟在数据库表里面定义这个表几乎没有区别,你不再需要各种查询,删除,插入,等等这些繁琐的事情。加一个表,你只需要定义个表即可。。我想,这就是优雅。。

比如,我要加播放记录,PlayRecord,那么,我实际操作过程中,只需要定义个类 PlayRecord{ String mMovieId; String mMovieTitle....}等字段信息即可。


好,定义好了什么叫优雅,我们就来完成这项优雅的事情。。

大家要记住一点,优雅 的让你少干活,不代表没人干这个活,这个活,可能在父类里面干完了。以后的子类,基本就子传父业即可。所以,不要说,怎么这么难。。


第一步,我们需要定义个相对而言很多人不清楚的一个类,标签辅助类。


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DatabaseField {

public enum DataType {
INTEGER, REAL, TEXT, BLOB
};

String value() default "";

DataType type() default DataType.TEXT;

String name() default "";

boolean isNull() default true;

boolean isPrimaryKey() default false;

boolean isAutoIncrement() default false;
}

很困惑是正常的,我们看过interface,这里却冒出个@interface,前辈告诉我,这叫标签类,类似于@override,而我们自定义了一个标签类,它的作用我们后面会讲到。起到一个辅助的作用。。

需要详细了解的同学,可以搜索 anotation interface。


第二步,定义个所有表格类的父类。

public abstract class BaseInfo {

@DatabaseField(name = "_id", type = DataType.INTEGER, isPrimaryKey = true, isAutoIncrement = true)
public long id;

@DatabaseField(name = "created_at", type = DataType.INTEGER, isNull = false)
public long createdAt;

@DatabaseField(name = "updated_at", type = DataType.INTEGER, isNull = false)
public long updatedAt;

public static String getTableName(Class clazz) {
String name = clazz.getSimpleName();
name = name.replaceAll("(?:(?<=[a-z])(?=[A-Z]))|(?:(?<=\\w)(?=[A-Z][a-z]))", "_");
return name.toLowerCase(Locale.US) + "s";
}

public BaseInfo() {
id = -1;
}

public boolean isNewRecord() {
return id == -1;
}

@Override
public boolean equals(Object o) {
BaseInfo info = (BaseInfo) o;

return info != null && info.id == id;
}
}

第一步的标签类初次出现在这里,这个父类定义了一些大家都需要定义的字段,例如id等。

后续的表格继承这个类。


第三步,定义个操作父类,把所有的数据库操作都封装在里面,然后后面继承它的子类就轻松很多。

public class BaseDao {
private static final Logger LOG = Logger.getLogger(BaseDao.class);

private Class mClazz;
private String mTableName;
private List mDbFields = new ArrayList();
private List mColumnNames = new ArrayList();
private int mMaxRowCount;
private DatabaseHelper mOpenHelper;

public BaseDao(Context context, Class clazz) {
this(context, clazz, -1);
}

public BaseDao(Context context, Class clazz, int maxRowCount) {
mClazz = clazz;
mTableName = BaseInfo.getTableName(clazz);
mMaxRowCount = maxRowCount;
mOpenHelper = DatabaseHelper.getInstance();

retrieveFieldInfos();
}

public void beginbeginTransaction() {
mOpenHelper.getWritableDatabase().beginTransaction();
}

public void setTransactionSuccessful() {
mOpenHelper.getWritableDatabase().setTransactionSuccessful();
}

public void endTransaction() {
mOpenHelper.getWritableDatabase().endTransaction();
}

public long insert(T data) {
return insert(data, null);
}

public long insert(T data, SQLiteDatabase database) {
long result = -1;
SQLiteDatabase db;
if (database == null) {
db = mOpenHelper.getWritableDatabase();
} else {
db = database;
}

// 超过条数限制则复用最后一个记录
if (mMaxRowCount > 0 && getRowCount(db) >= mMaxRowCount) {
data.id = last().id;
data.createdAt = getCurrentTimestamp();

update(data);
} else {
try {
ContentValues values = new ContentValues();

data.createdAt = getCurrentTimestamp();
data.updatedAt = data.createdAt;

for (Field field : mDbFields) {
DatabaseField dbFieldAnnotation = field.getAnnotation(DatabaseField.class);
if (dbFieldAnnotation != null) {
if (!(field.getName().equals("id"))) {
setFieldValue(data, values, field, dbFieldAnnotation);
}
}
}

result = db.insert(mTableName, null, values);
} catch (IllegalAccessException e) {
LOG.warn(e);
}
}

return result;
}

public T first() {
T result = null;

List values = find(null, null, null, null, "`updated_at` DESC");
if (!(values.isEmpty())) {
result = values.get(0);
}

return result;
}

public T last() {
T result = null;

List values = find(null, null, null, null, "`updated_at` ASC");
if (!(values.isEmpty())) {
result = values.get(0);
}

return result;
}

public int getRowCount() {
return getRowCount(null);
}

public boolean isMaxRowCount() {
return getRowCount() >= mMaxRowCount;
}

public int getRowCount(SQLiteDatabase database) {
SQLiteDatabase db = null;
if (database == null) {
db = mOpenHelper.getReadableDatabase();
} else {
db = database;
}
return db.query(mTableName, null, null, null, null, null, null).getCount();

}

public int delete(long id) {
return deleteBy("_id", Long.toString(id));
}

public int deleteBy(String columnName, String value) {
return mOpenHelper.getWritableDatabase()
.delete(mTableName, escapeColumnName(columnName) + " = ?", new String[] { value });
}

public int update(T data) {
int result = 0;

BaseInfo info = (BaseInfo) data;

try {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
ContentValues values = new ContentValues();

data.updatedAt = getCurrentTimestamp();

for (Field field : mDbFields) {
DatabaseField annotation = field.getAnnotation(DatabaseField.class);
if (annotation != null) {
setFieldValue(data, values, field, annotation);
}
}

result = db.update(mTableName, values, "`_id` = ?", new String[] { Long.toString(info.id) });
} catch (IllegalAccessException e) {
LOG.warn(e);
}

return result;
}

public T find(int id) {
T instance = null;

SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = db.query(mTableName, null, "`_id` = ?", new String[] { Long.toString(id) }, null, null, null);
if (c != null && c.moveToNext()) {
instance = this.fillData(c);
}
if (c != null) {
c.close();
}

return instance;
}

public List findAll() {
return find("", null, null, null, null);
}

public List findAllOrderByUpdatedAt() {
return find("", null, null, null, "`updated_at` DESC");
}

public T find(String selection, String[] selectArgs, String sortOrder) {
List dataList = find(selection, selectArgs, null, null, sortOrder);

return dataList.isEmpty() ? null : dataList.get(0);
}

public T findBy(String columnName, String value) {
List dataList = find(escapeColumnName(columnName) + " = ?", new String[] { value }, null, null, null);

return dataList.isEmpty() ? null : dataList.get(0);
}

public List find(String selection, String[] selectArgs, String groupBy, String having, String sortOrder) {
List dataList = null;

SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = db.query(mTableName, null, selection, selectArgs, groupBy, having, sortOrder);
if (c != null) {
dataList = fillList(c);
c.close();
}

return dataList;
}

public Cursor query(String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
return mOpenHelper.getReadableDatabase()
.query(mTableName, null, selection, selectionArgs, groupBy, having, orderBy);
}

public boolean exist(String selection, String[] selectionArgs) {
boolean result = false;

Cursor cursor = mOpenHelper.getReadableDatabase()
.query(mTableName, null, selection, selectionArgs, null, null, null);
result = cursor.getCount() > 0;
cursor.close();

return result;
}

public void touch(T record) {
update(record);
}

public String getTableName() {
return mTableName;
}

public static String getColumnName(Field field, DatabaseField annoation) {
String name = annoation.name();
if (TextUtils.isEmpty(name)) {
name = field.getName();
}

return name;
}

@SuppressWarnings("unchecked")
public Class getClassT() {
Type type = getClass().getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) type;

return (Class) parameterizedType.getActualTypeArguments()[0];
}

public List fillList(Cursor cursor) {
List dataList = new ArrayList();
while (cursor.moveToNext()) {
T entry = fillData(cursor);
if (entry != null) {
dataList.add(entry);
}
}

return dataList;
}

public Object getFieldValue(Cursor cursor, DatabaseField annoation, String columnName) {
Object result = null;
final int columnIndex = cursor.getColumnIndex(columnName);
if (columnIndex != -1) {
switch (annoation.type()) {
case TEXT:
result = cursor.getString(columnIndex);
break;

case BLOB:
break;

case INTEGER:
result = cursor.getLong(columnIndex);
break;

case REAL:
result = cursor.getFloat(columnIndex);
break;

default:
break;
}
}
return result;
}

public T fillData(Cursor cursor) {
T instance = null;
try {
instance = mClazz.newInstance();
} catch (IllegalAccessException e) {
LOG.warn(e);
} catch (InstantiationException e) {
LOG.warn(e);
}
for (Field field : mDbFields) {
DatabaseField annoation = field.getAnnotation(DatabaseField.class);
if (annoation != null) {
String columnName = getColumnName(field, annoation);
try {
Object value = getFieldValue(cursor, annoation, columnName);
if (value != null) {
if (field.getType().equals(int.class)) {
field.set(instance, ((Long) value).intValue());
} else {
field.set(instance, value);
}
}
} catch (IllegalArgumentException e) {
LOG.warn(e);
} catch (IllegalAccessException e) {
LOG.warn(e);
}
}
}

return instance;
}

private void retrieveFieldInfos() {
Field[] fields = mClazz.getFields();
for (Field field : fields) {
DatabaseField annotation = field.getAnnotation(DatabaseField.class);
if (annotation != null) {
mDbFields.add(field);
mColumnNames.add(getColumnName(field, annotation));
}
}
}

private void setFieldValue(T data, ContentValues values, Field field, DatabaseField annotation)
throws IllegalAccessException {
String columnName = escapeColumnName(getColumnName(field, annotation));
switch (annotation.type()) {
case TEXT:
values.put(columnName, String.valueOf(field.get(data)));
break;

case BLOB:
break;

case INTEGER:
Object o = field.get(data);
if (o instanceof Integer) {
values.put(columnName, (Integer) o);
} else {
values.put(columnName, (Long) o);
}
break;

case REAL:
values.put(columnName, (Float) field.get(data));
break;

default:
break;
}
}

private String escapeColumnName(String columnName) {
return "`" + columnName + "`";
}

private long getCurrentTimestamp() {
return (new Date()).getTime();
}
}

对数据库的一些改变表的,例如删除,更新的操作放到一个单独 的类中。


public class DatabaseHelper extends SQLiteOpenHelper {
private static final Logger LOG = Logger.getLogger(DatabaseHelper.class);

private static final String DATABASE_NAME = "anime.db";
private static final int DATABASE_VERSION = 3;

private static List> sTables = new ArrayList>();

private static DatabaseHelper sInstance = null;
private SQLiteDatabase mDatabase = null;

public static void init(Context context) {
assert (sInstance == null);

LOG.info("init.");

registerDatabaseTable(LocalPlayRecord.class);
registerDatabaseTable(PlayRecord.class);
registerDatabaseTable(MovieUpdateInfo.class);
registerDatabaseTable(DownloadInfo.class);

registerDatabaseTable(SearchKeyword.class);
registerDatabaseTable(SearchResult.class);
registerDatabaseTable(SearchRecord.class);
registerDatabaseTable(Favorite.class);
registerDatabaseTable(TestTable.class);

sInstance = new DatabaseHelper(context);
}

public static void fini() {
LOG.info("fini.");

if (sTables.size() > 0) {
sTables.clear();
}
sInstance.close();
sInstance = null;
}

public static DatabaseHelper getInstance() {
return sInstance;
}

public void close() {
dropTable(mDatabase, SearchKeyword.class);
dropTable(mDatabase, SearchResult.class);
createTable(mDatabase, SearchKeyword.class);
createTable(mDatabase, SearchResult.class);

mDatabase.close();
}

@Override
public void onCreate(SQLiteDatabase db) {
LOG.debug("onCreate.");

createTables(db);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
LOG.info("onUpgrade. {} => {}", oldVersion, newVersion);

if (oldVersion <3) {
upgrade(db, oldVersion);
} else {
createTables(db);
}
}

private void upgrade(SQLiteDatabase db, int oldVersion) {
try {
String tableName = null;
String tempTableName = null;
String sql;
db.beginTransaction();
if (oldVersion == 1) {
tableName = BaseInfo.getTableName(Favorite.class);
sql = "ALTER TABLE es RENAME TO " + tableName;
db.execSQL(sql);

tableName = BaseInfo.getTableName(MovieUpdateInfo.class);
sql = "ALTER TABLE gs RENAME TO " + tableName;
db.execSQL(sql);

tableName = BaseInfo.getTableName(SearchKeyword.class);
sql = "ALTER TABLE ks RENAME TO " + tableName;
db.execSQL(sql);

tableName = BaseInfo.getTableName(SearchRecord.class);
sql = "ALTER TABLE ms RENAME TO " + tableName;
db.execSQL(sql);

tableName = BaseInfo.getTableName(SearchResult.class);
sql = "ALTER TABLE os RENAME TO " + tableName;
db.execSQL(sql);

tableName = BaseInfo.getTableName(PlayRecord.class);
tempTableName = tableName + "_temp";
sql = "ALTER TABLE iis RENAME TO " + tempTableName;
db.execSQL(sql);

createTables(db);

StringBuilder columnsBuilder = new StringBuilder();
Field[] fields = PlayRecord.class.getFields();
for (Field field : fields) {
DatabaseField annotation = field.getAnnotation(DatabaseField.class);
if (annotation != null) {
String columnName = BaseDao.getColumnName(field, annotation);

if (!"_id".equals(columnName) && !"source".equals(columnName)) {
columnsBuilder.append(columnName).append(',');
}
}
}
columnsBuilder.deleteCharAt(columnsBuilder.length() - 1);

String columns = columnsBuilder.toString();
sql = "INSERT INTO " + tableName + "(" + columns + ")"
+ " SELECT " + columns + " FROM " + tempTableName;
db.execSQL(sql);

dropTable(db, tempTableName);
} else if (oldVersion == 2) {
createTables(db);

tableName = BaseInfo.getTableName(LocalPlayRecord.class);
StringBuffer sqlBuffer = new StringBuffer();
Field[] localFields = LocalPlayRecord.class.getFields();
for (Field field : localFields) {
DatabaseField annotation = field.getAnnotation(DatabaseField.class);
if (annotation != null) {
String fieldName = BaseDao.getColumnName(field, annotation);
if (fieldName.equals("total_episode")) {
DataType tableType = annotation.type();
sqlBuffer.append(" " + fieldName + " " + tableType.toString() + " ");
if (!(annotation.isNull())) {
sqlBuffer.append("NOT NULL ");
}
if (annotation.isPrimaryKey()) {
sqlBuffer.append("PRIMARY KEY ");
}
if (annotation.isAutoIncrement()) {
sqlBuffer.append("autoincrement");
}

break;
}
}
}
sql = "ALTER TABLE " + tableName + " ADD COLUMN " + sqlBuffer.toString();
db.execSQL(sql);
}

db.setTransactionSuccessful();
} catch (SQLException e) {
LOG.warn(e);
} catch (Exception e) {
LOG.warn(e);
} finally {
db.endTransaction();
}
}

private static void registerDatabaseTable(Class tableClass) {
LOG.info("register {}.", tableClass.getName());

if (!(sTables.contains(tableClass))) {
sTables.add(tableClass);
}
}

private DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);

mDatabase = getWritableDatabase();

LOG.debug("construction. database.version={}", DATABASE_VERSION);
}

private void dropTable(SQLiteDatabase db, Class tableClass) {
dropTable(db, BaseInfo.getTableName(tableClass));
}

private void dropTable(SQLiteDatabase db, String tableName) {
db.execSQL("DROP TABLE IF EXISTS " + tableName);
}

private void createTable(SQLiteDatabase db, Class tableClass) {
StringBuffer sqlBuffer = new StringBuffer();
sqlBuffer.append("CREATE TABLE IF NOT EXISTS " + BaseInfo.getTableName(tableClass) + " ( ");
Field[] fields = tableClass.getFields();

if (fields != null && fields.length > 0) {
for (int i = 0; i Field field = fields[i];
if (field != null) {
DatabaseField annoation = field.getAnnotation(DatabaseField.class);
if (annoation != null) {
String fieldName = BaseDao.getColumnName(field, annoation);
DataType tableType = annoation.type();
sqlBuffer.append("`" + fieldName + "` " + tableType.toString() + " ");
if (!(annoation.isNull())) {
sqlBuffer.append("NOT NULL ");
}
if (annoation.isPrimaryKey()) {
sqlBuffer.append("PRIMARY KEY ");
}
if (annoation.isAutoIncrement()) {
sqlBuffer.append("autoincrement");
}
sqlBuffer.append(",");
}
}
}
}
// 删除最后一个逗号,
sqlBuffer.deleteCharAt(sqlBuffer.length() - 1);

sqlBuffer.append(" )");
String sql = sqlBuffer.toString();
LOG.info("create table. sql={}", sql);
db.execSQL(sql);
}

private void createTables(SQLiteDatabase db) {
for (Class tableClass : sTables) {
createTable(db, tableClass);
}
}
}

后续,假如我们需要定义播放记录表。。则,变得非常便捷。。


1)表类

public class PlayRecord extends BaseInfo {

@DatabaseField(type = DataType.TEXT, isNull = false)
public String name;

@DatabaseField(type = DataType.INTEGER, isNull = false)
public int type;

@DatabaseField(name = "movie_id", type = DataType.INTEGER, isNull = false)
public int movieId;

@DatabaseField(type = DataType.TEXT)
public String source;

@DatabaseField(type = DataType.INTEGER, isNull = false)
public int episode;

@DatabaseField(name = "total_episode", type = DataType.INTEGER, isNull = false)
public int totalEpisode;

@DatabaseField(type = DataType.INTEGER, isNull = false)
public int position;

@DatabaseField(type = DataType.INTEGER, isNull = false)
public int duration;

public boolean isFinished() {
return duration > 0 && position == duration;
}

@Override
public String toString() {
return "[name=" + name + " movieId=" + movieId + " type=" + type + " source=" + source + " totalEpisode="
+ totalEpisode + " episode=" + episode + " position=" + position + " duration=" + duration + "]";
}
}


2)操作类

public class PlayRecordDao extends BaseDao {
@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(PlayRecordDao.class);

private static final int MAX_ROW_COUNT = 20;

public PlayRecordDao(Context context) {
super(context, PlayRecord.class, MAX_ROW_COUNT);
}

public boolean exist(int movieId) {
return findByMovieId(movieId) != null;
}

public PlayRecord getPlayRecord(int movieId) {
PlayRecord record = findByMovieId(movieId);

if (record == null) {
record = new PlayRecord();
record.movieId = movieId;
}

return record;
}

public PlayRecord getPlayRecord(int movieId, int episode) {
PlayRecord record = getPlayRecord(movieId);

if (record.isNewRecord()) {
record.episode = episode;
} else if (record.episode != episode) {
record.episode = episode;
record.position = 0;
}

return record;
}

public PlayRecord save(PlayRecord record) {
return save(record, null);
}

private PlayRecord save(PlayRecord record, SQLiteDatabase db) {
if (record.isNewRecord()) {
record.id = insert(record, db);
} else {
update(record);
}

return record;
}

public int deleteByMovieId(long movieId) {
return deleteBy("movie_id", String.valueOf(movieId));
}

public PlayRecord findByMovieId(int movieId) {
return findBy("movie_id", Integer.toString(movieId));
}

}


举一反三。

假如我们要定义用户偏好的一个表。也是如此进行。非常简单。

1)表格类定义

public class Favorite extends BaseInfo {
@DatabaseField(type = DataType.TEXT, isNull = false)
public String name;

@DatabaseField(name = "movie_id", type = DataType.INTEGER, isNull = false)
public int movieId;

@DatabaseField(name = "poster_url", type = DataType.TEXT, isNull = false)
public String posterUrl;

@DatabaseField(name = "release_date", type = DataType.TEXT, isNull = false)
public String releaseDate;

@DatabaseField(name = "tags", type = DataType.TEXT, isNull = false)
public String tags;

public Favorite() {
}

public Favorite(String name, int movieId, String posterUrl, long releaseDate, String tags) {
this.name = name;
this.movieId = movieId;
this.posterUrl = posterUrl;
this.releaseDate = String.valueOf(releaseDate);
this.tags = tags;
}

@Override
public String toString() {
return "[name=" + name + " movieId=" + movieId + " posterUrl=" + posterUrl + "]";
}

}

2)操作定义

public class FavoriteDao extends BaseDao {
public static final int MAX_COUNT = 100;

public FavoriteDao(Context context) {
super(context, Favorite.class, MAX_COUNT);
}

public long save(Favorite favorite) {
if (favorite.isNewRecord()) {
favorite.id = insert(favorite, null);
} else {
update(favorite);
}

return favorite.id;
}

public boolean exist(Favorite favorite) {
return findByMovieId(favorite.movieId) != null;
}

public int delete(Favorite favorite) {
return deleteBy("movie_id", String.valueOf(favorite.movieId));
}

public Favorite findByMovieId(int movieId) {
return findBy("movie_id", String.valueOf(movieId));
}

}


以此类推。。。。。

写父类抽象的人很累,但是,一劳永逸。。






推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
author-avatar
王之玉58_913
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有