diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index c7c5968a..efaca07f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -41,7 +41,7 @@ import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadReceiver; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.DbUtil; +import com.arialyy.aria.orm.DelegateWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.io.File; @@ -77,7 +77,7 @@ import org.xml.sax.SAXException; private Configuration.UploadConfig mUConfig; private AriaManager(Context context) { - DbUtil.init(context.getApplicationContext()); + DelegateWrapper.init(context.getApplicationContext()); APP = context.getApplicationContext(); regAppLifeCallback(context); initConfig(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java index 3f3efe11..cd0197fb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java @@ -16,13 +16,7 @@ package com.arialyy.aria.core.command.normal; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.core.upload.UploadTaskEntity; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; -import java.util.List; /** * Created by AriaL on 2017/6/27. @@ -44,46 +38,5 @@ public class CancelAllCmd extends AbsNormalCmd { @Override public void executeCmd() { removeAll(); - if (mTaskEntity instanceof DownloadTaskEntity - || mTaskEntity instanceof DownloadGroupTaskEntity) { - handleDownloadRemove(); - handleDownloadGroupRemove(); - } else if (mTaskEntity instanceof UploadTaskEntity) { - handleUploadRemove(); - handleUploadRemove(); - } - } - - /** - * 处理下载任务组的删除操作 - */ - private void handleDownloadGroupRemove() { - List allEntity = DbEntity.findAllData(DownloadGroupTaskEntity.class); - if (allEntity == null || allEntity.size() == 0) return; - for (DownloadGroupTaskEntity entity : allEntity) { - CommonUtil.delDownloadGroupTaskConfig(removeFile, entity); - } - } - - /** - * 处理上传的删除 - */ - private void handleUploadRemove() { - List allEntity = DbEntity.findAllData(UploadTaskEntity.class); - if (allEntity == null || allEntity.size() == 0) return; - for (UploadTaskEntity entity : allEntity) { - CommonUtil.delUploadTaskConfig(removeFile, entity); - } - } - - /** - * 处理下载的删除 - */ - private void handleDownloadRemove() { - List allEntity = DbEntity.findAllData(DownloadTaskEntity.class); - if (allEntity == null || allEntity.size() == 0) return; - for (DownloadTaskEntity entity : allEntity) { - CommonUtil.delDownloadTaskConfig(removeFile, entity); - } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java index 82120c32..62824b10 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java @@ -77,7 +77,6 @@ class StartCmd extends AbsNormalCmd { || task.getState() == IEntity.STATE_OTHER || task.getState() == IEntity.STATE_POST_PRE || task.getState() == IEntity.STATE_COMPLETE) { - //startTask(); resumeTask(); } else { sendWaitState(); @@ -85,7 +84,6 @@ class StartCmd extends AbsNormalCmd { } } else { if (!task.isRunning()) { - //startTask(); resumeTask(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java index cef4ea3b..c8868aeb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java @@ -18,11 +18,13 @@ package com.arialyy.aria.core.common; import android.content.Context; import android.util.SparseArray; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -89,7 +91,6 @@ public abstract class AbsFileer extends AbsDownloadTarget { - private static final String TAG = "BaseGroupTarget"; /** * 组任务名 @@ -60,7 +58,6 @@ abstract class BaseGroupTarget public TARGET setGroupAlias(String alias) { if (TextUtils.isEmpty(alias)) return (TARGET) this; mEntity.setAlias(alias); - mEntity.update(); return (TARGET) this; } @@ -158,7 +155,6 @@ abstract class BaseGroupTarget } mEntity.setDirPath(dirPath); reChangeDirPath(dirPath); - mEntity.update(); } return true; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java index d26f86f2..141d5fb3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java @@ -29,7 +29,6 @@ import java.io.File; abstract class BaseNormalTarget extends AbsDownloadTarget { - private static final String TAG = "BaseNormalTarget"; /** * 资源地址 */ @@ -42,9 +41,6 @@ abstract class BaseNormalTarget this.url = url; mTargetName = targetName; mTaskEntity = TEManager.getInstance().getTEntity(DownloadTaskEntity.class, url); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(DownloadTaskEntity.class, url); - } mEntity = mTaskEntity.entity; mTaskEntity.refreshInfo = refreshInfo; if (mEntity != null) { @@ -110,7 +106,11 @@ abstract class BaseNormalTarget * @return {@code true}合法 */ @Override protected boolean checkEntity() { - return getTargetType() < GROUP_HTTP && checkUrl() && checkFilePath(); + boolean b = getTargetType() < GROUP_HTTP && checkUrl() && checkFilePath(); + if (b) { + mTaskEntity.save(mEntity); + } + return b; } /** @@ -149,7 +149,7 @@ abstract class BaseNormalTarget mEntity.setDownloadPath(filePath); mEntity.setFileName(newFile.getName()); mTaskEntity.key = filePath; - mTaskEntity.update(); + //mTaskEntity.update(); CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java index 432015ac..89704153 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java @@ -21,8 +21,8 @@ import android.os.Parcelable; import android.text.TextUtils; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.Foreign; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Primary; import com.arialyy.aria.util.CommonUtil; /** @@ -35,12 +35,7 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { /** * 所属任务组 */ - @Foreign(table = DownloadGroupEntity.class, column = "groupName") private String groupName = ""; - - /** - * 下载任务实体的key - */ - @Foreign(table = DownloadTaskEntity.class, column = "key") private String taskKey = ""; + @Foreign(parent = DownloadGroupEntity.class, column = "groupName") private String groupName = ""; /** * 从服务器的返回信息中获取的文件md5信息,如果服务器没有返回,则不会设置该信息 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index c6697a5e..d90b05eb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -18,8 +18,7 @@ package com.arialyy.aria.core.download; import android.os.Parcel; import com.arialyy.aria.core.inf.AbsGroupEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.OneToMany; -import java.util.ArrayList; +import com.arialyy.aria.orm.annotation.Ignore; import java.util.List; /** @@ -28,8 +27,11 @@ import java.util.List; */ public class DownloadGroupEntity extends AbsGroupEntity { - @OneToMany(table = DownloadEntity.class, key = "groupName") private List subtask = - new ArrayList<>(); + //@OneToMany(table = DownloadEntity.class, key = "groupName") private List subtask = + // new ArrayList<>(); + + @Ignore + private List subtask; /** * 任务组下载文件的文件夹地址 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java index 07ef9efb..1d2b0a8d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java @@ -24,16 +24,13 @@ import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.WeakHashMap; /** * Created by AriaL on 2017/6/29. * 下载任务组 */ public class DownloadGroupTarget extends BaseGroupTarget { - private final String TAG = "DownloadGroupTarget"; /** * 子任务下载地址, */ @@ -50,12 +47,8 @@ public class DownloadGroupTarget extends BaseGroupTarget { this.mUrls.addAll(groupEntity.getUrls()); } mGroupName = CommonUtil.getMd5Code(groupEntity.getUrls()); - mTaskEntity = TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, mGroupName); - if (mTaskEntity == null) { - mTaskEntity = - TEManager.getInstance().createTEntity(DownloadGroupTaskEntity.class, groupEntity); - } - mEntity = mTaskEntity.entity; + mTaskEntity = TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, mUrls); + mEntity = mTaskEntity.getEntity(); if (mEntity != null) { mDirPathTemp = mEntity.getDirPath(); @@ -66,11 +59,8 @@ public class DownloadGroupTarget extends BaseGroupTarget { this.mTargetName = targetName; this.mUrls = urls; mGroupName = CommonUtil.getMd5Code(urls); - mTaskEntity = TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, mGroupName); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createGTEntity(DownloadGroupTaskEntity.class, mUrls); - } - mEntity = mTaskEntity.entity; + mTaskEntity = TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, mUrls); + mEntity = mTaskEntity.getEntity(); } /** @@ -88,7 +78,6 @@ public class DownloadGroupTarget extends BaseGroupTarget { } if (mEntity.getFileSize() <= 1 || mEntity.getFileSize() != fileSize) { mEntity.setFileSize(fileSize); - mEntity.update(); } return this; } @@ -137,7 +126,8 @@ public class DownloadGroupTarget extends BaseGroupTarget { entity.setGroupName(mGroupName); entity.setGroupChild(true); entity.setFileName(fileName); - entity.insert(); + // TODO: 2018/4/3 是否需要在这个插入? + //entity.insert(); list.add(entity); } return list; @@ -164,7 +154,6 @@ public class DownloadGroupTarget extends BaseGroupTarget { //文件夹路径通过后,并且该实体没有子任务,则创建子任务 if (mEntity.getSubTask() == null || mEntity.getSubTask().isEmpty()) { mEntity.setSubTasks(createSubTask()); - mTaskEntity.update(); } else { updateSingleSubFileName(); } @@ -230,7 +219,6 @@ public class DownloadGroupTarget extends BaseGroupTarget { } mEntity.setGroupName(CommonUtil.getMd5Code(mUrls)); - mEntity.update(); return true; } @@ -251,7 +239,8 @@ public class DownloadGroupTarget extends BaseGroupTarget { "UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); entity.setDownloadPath(newPath); entity.setFileName(newName); - entity.update(); + // TODO: 2018/4/3 是否需要在这个更新? + //entity.update(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java index 72ecdc31..bd24c154 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java @@ -16,24 +16,42 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.inf.AbsGroupTaskEntity; -import com.arialyy.aria.orm.OneToOne; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.Primary; /** * Created by AriaL on 2017/7/1. */ public class DownloadGroupTaskEntity extends AbsGroupTaskEntity { - @OneToOne(table = DownloadGroupEntity.class, key = "groupName") public DownloadGroupEntity entity; + @Ignore private DownloadGroupEntity entity; + + @Primary @Foreign(parent = DownloadGroupEntity.class, column = "groupName") + private String key; @Override public DownloadGroupEntity getEntity() { return entity; } + public void setEntity(DownloadGroupEntity entity) { + this.entity = entity; + } + + @Override public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } - public void save(DownloadGroupEntity groupEntity){ - key = groupEntity.getKey(); - entity = groupEntity; - groupEntity.save(); + public void save(DownloadGroupEntity groupEntity) { + if (groupEntity != null) { + key = groupEntity.getKey(); + entity = groupEntity; + groupEntity.save(); + } save(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskWrapper.java new file mode 100644 index 00000000..c0f3fbfd --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskWrapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download; + +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + */ +@Wrapper +public class DownloadGroupTaskWrapper extends AbsWrapper { + + @One + public DownloadGroupEntity entity; + + @Many(parentColumn = "key", entityColumn = "groupName") + private List taskEntitys; + + public DownloadGroupTaskEntity taskEntity; + + @Override protected void handleConvert() { + taskEntity = (taskEntitys == null || taskEntitys.isEmpty()) ? null : taskEntitys.get(0); + if (taskEntity != null) { + taskEntity.setEntity(entity); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupWrapper.java new file mode 100644 index 00000000..e5a1e9d9 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupWrapper.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download; + +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + */ +@Wrapper +public class DownloadGroupWrapper extends AbsWrapper { + + @One + public DownloadGroupEntity groupEntity; + + @Many(parentColumn = "groupName", entityColumn = "groupName") + public List subEntity; + + @Override protected void handleConvert() { + if (subEntity != null && !subEntity.isEmpty()) { + groupEntity.setSubTasks(subEntity); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index b8101a36..b65f623f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -25,9 +25,11 @@ import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers; import com.arialyy.aria.core.scheduler.DownloadSchedulers; import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import java.util.ArrayList; @@ -106,14 +108,14 @@ public class DownloadReceiver extends AbsReceiver { * */ @Deprecated public DownloadTarget load(@NonNull String url, boolean refreshInfo) { - url = CheckUtil.checkUrl(url); + CheckUtil.checkUrlInvalidThrow(url); return new DownloadTarget(url, targetName, refreshInfo); } /** * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 * - * @param urls 人刷谁 + * @param urls 任务组子任务下载地址列表 */ @Deprecated public DownloadGroupTarget load(List urls) { @@ -173,7 +175,7 @@ public class DownloadReceiver extends AbsReceiver { * @param refreshInfo 是否刷新下载信息 */ public FtpDownloadTarget loadFtp(@NonNull String url, boolean refreshInfo) { - url = CheckUtil.checkUrl(url); + CheckUtil.checkUrlInvalidThrow(url); return new FtpDownloadTarget(url, targetName, refreshInfo); } @@ -203,7 +205,7 @@ public class DownloadReceiver extends AbsReceiver { * 加载ftp文件夹下载地址 */ public FtpDirDownloadTarget loadFtpDir(@NonNull String dirUrl) { - dirUrl = CheckUtil.checkUrl(dirUrl); + CheckUtil.checkUrlInvalidThrow(dirUrl); return new FtpDirDownloadTarget(dirUrl, targetName); } @@ -257,39 +259,55 @@ public class DownloadReceiver extends AbsReceiver { /** * 通过下载链接获取下载实体 + * + * @return 如果url错误或查找不到数据,则返回null */ public DownloadEntity getDownloadEntity(String downloadUrl) { - downloadUrl = CheckUtil.checkUrl(downloadUrl); + if (CheckUtil.checkUrl(downloadUrl)) { + return null; + } return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); } /** - * 通过下载链接获取保存在数据库的下载任务实体 + * 通过下载地址和文件保存路径获取下载任务实体 + * + * @param downloadUrl 下载地址 + * @return 如果url错误或查找不到数据,则返回null */ public DownloadTaskEntity getDownloadTask(String downloadUrl) { - downloadUrl = CheckUtil.checkUrl(downloadUrl); - DownloadEntity entity = getDownloadEntity(downloadUrl); - if (entity == null || TextUtils.isEmpty(entity.getDownloadPath())) return null; - return DbEntity.findFirst(DownloadTaskEntity.class, "key=? and isGroupTask='false'", - entity.getDownloadPath()); + if (CheckUtil.checkUrl(downloadUrl)) { + return null; + } + return TEManager.getInstance().getTEntity(DownloadTaskEntity.class, downloadUrl); } /** * 通过下载链接获取保存在数据库的下载任务组实体 + * + * @param urls 任务组子任务下载地址列表 + * @return 返回对应的任务组实体;如果查找不到对应的数据或子任务列表为null,返回null */ - public DownloadGroupTaskEntity getDownloadGroupTask(List urls) { - CheckUtil.checkDownloadUrls(urls); - String hashCode = CommonUtil.getMd5Code(urls); - return DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", hashCode); + public DownloadGroupTaskEntity getGroupTask(List urls) { + if (urls == null || urls.isEmpty()) { + ALog.e(TAG, "获取任务组实体失败:任务组子任务下载地址列表为null"); + return null; + } + return TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, urls); } /** - * 通过任务组key,获取任务组实体 - * 如果是http,key为所有子任务下载地址拼接后取md5 - * 如果是ftp,key为ftp服务器的文件夹路径 + * 获取FTP文件夹下载任务实体 + * + * @param dirPath FTP文件夹本地保存路径 + * @return 返回对应的任务组实体;如果查找不到对应的数据或路径为null,返回null */ - public DownloadGroupTaskEntity getDownloadGroupTask(String key) { - return DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", key); + public DownloadGroupTaskEntity getFtpDirTask(String dirPath) { + if (TextUtils.isEmpty(dirPath)) { + ALog.e(TAG, "获取FTP文件夹实体失败:文件夹路径为null"); + return null; + } + return TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, dirPath); } /** @@ -327,15 +345,25 @@ public class DownloadReceiver extends AbsReceiver { /** * 获取任务组列表 + * + * @return 如果没有任务组列表,则返回null */ public List getGroupTaskList() { - return DownloadEntity.findAllData(DownloadGroupEntity.class); + List wrappers = DbEntity.findRelationData(DownloadGroupWrapper.class); + if (wrappers == null || wrappers.isEmpty()) { + return null; + } + List entities = new ArrayList<>(); + for (DownloadGroupWrapper wrapper : wrappers) { + entities.add(wrapper.groupEntity); + } + return entities; } /** * 获取普通任务和任务组的任务列表 */ - public List getTotleTaskList() { + public List getTotalTaskList() { List list = new ArrayList<>(); List simpleTask = getTaskList(); List groupTask = getGroupTaskList(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java index 8f608f00..e7bd9b0f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java @@ -27,7 +27,6 @@ import java.util.Map; */ public class DownloadTarget extends BaseNormalTarget implements IHttpHeaderTarget { - private static final String TAG = "DownloadTarget"; private HttpHeaderDelegate mDelegate; DownloadTarget(DownloadEntity entity, String targetName) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java index 32e2dcb1..a089da27 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java @@ -16,17 +16,19 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.inf.AbsNormalTaskEntity; -import com.arialyy.aria.orm.Ignore; -import com.arialyy.aria.orm.NoNull; -import com.arialyy.aria.orm.OneToOne; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.NoNull; +import com.arialyy.aria.orm.annotation.Primary; /** * Created by lyy on 2017/1/23. - * 下载任务实体 + * 下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 */ public class DownloadTaskEntity extends AbsNormalTaskEntity { - @OneToOne(table = DownloadEntity.class, key = "downloadPath") public DownloadEntity entity; + //@OneToOne(table = DownloadEntity.class, key = "downloadPath") public DownloadEntity entity; + @Ignore public DownloadEntity entity; /** * 任务的url @@ -48,6 +50,11 @@ public class DownloadTaskEntity extends AbsNormalTaskEntity { */ public boolean isGroupTask = false; + /** + * Task实体对应的key + */ + @Primary @Foreign(parent = DownloadEntity.class, column = "downloadPath") public String key = ""; + public DownloadTaskEntity() { } @@ -55,6 +62,10 @@ public class DownloadTaskEntity extends AbsNormalTaskEntity { return entity; } + @Override public String getKey() { + return key; + } + public void save(DownloadEntity entity) { this.entity = entity; if (entity != null) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskWrapper.java new file mode 100644 index 00000000..a2fe9137 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskWrapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download; + +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + */ +@Wrapper +public class DownloadTaskWrapper extends AbsWrapper { + + @One + public DownloadEntity entity; + + @Many(parentColumn = "downloadPath", entityColumn = "key") + private List taskEntitys = null; + + public DownloadTaskEntity taskEntity; + + @Override public void handleConvert() { + taskEntity = (taskEntitys == null || taskEntitys.isEmpty()) ? null : taskEntitys.get(0); + if (taskEntity != null) { + taskEntity.entity = entity; + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java index b71c8b35..7b82e2fd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java @@ -28,7 +28,6 @@ import com.arialyy.aria.util.ALog; */ public class FtpDirDownloadTarget extends BaseGroupTarget implements IFtpTarget { - private final String TAG = "FtpDirDownloadTarget"; private FtpDelegate mDelegate; FtpDirDownloadTarget(String url, String targetName) { @@ -39,11 +38,8 @@ public class FtpDirDownloadTarget extends BaseGroupTarget private void init(String key) { mGroupName = key; mTaskEntity = TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, key); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(DownloadGroupTaskEntity.class, key); - } mTaskEntity.requestType = AbsTaskEntity.D_FTP_DIR; - mEntity = mTaskEntity.entity; + mEntity = mTaskEntity.getEntity(); if (mEntity != null) { mDirPathTemp = mEntity.getDirPath(); } @@ -55,7 +51,11 @@ public class FtpDirDownloadTarget extends BaseGroupTarget } @Override protected boolean checkEntity() { - return getTargetType() == GROUP_FTP_DIR && checkDirPath() && checkUrl(); + boolean b = getTargetType() == GROUP_FTP_DIR && checkDirPath() && checkUrl(); + if (b) { + mTaskEntity.save(mEntity); + } + return b; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java index 35ce167c..3b1e5b54 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java @@ -27,7 +27,6 @@ import com.arialyy.aria.util.CommonUtil; */ public class FtpDownloadTarget extends BaseNormalTarget implements IFtpTarget { - private final String TAG = "FtpDownloadTarget"; private FtpDelegate mDelegate; FtpDownloadTarget(DownloadEntity entity, String targetName, boolean refreshInfo) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java index 52e56a5d..012e9fc3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java @@ -106,7 +106,7 @@ public abstract class AbsGroupUtil implements IUtil { mGTEntity = groupEntity; mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mGTEntity.key); + DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mGTEntity.getKey()); if (tasks != null && !tasks.isEmpty()) { for (DownloadTaskEntity te : tasks) { te.removeFile = mGTEntity.removeFile; @@ -114,10 +114,10 @@ public abstract class AbsGroupUtil implements IUtil { mTasksMap.put(te.getEntity().getUrl(), te); } } - mGroupSize = mGTEntity.entity.getSubTask().size(); + mGroupSize = mGTEntity.getEntity().getSubTask().size(); mTotalLen = groupEntity.getEntity().getFileSize(); isNeedLoadFileSize = mTotalLen <= 1; - for (DownloadEntity entity : mGTEntity.entity.getSubTask()) { + for (DownloadEntity entity : mGTEntity.getEntity().getSubTask()) { File file = new File(entity.getDownloadPath()); if (entity.getState() == IEntity.STATE_COMPLETE && file.exists()) { mCompleteNum++; @@ -190,9 +190,10 @@ public abstract class AbsGroupUtil implements IUtil { * @param url 子任务下载地址 */ public void cancelSubTask(String url) { - List urls = mGTEntity.entity.getUrls(); + List urls = mGTEntity.getEntity().getUrls(); if (urls != null && !urls.isEmpty() && urls.contains(url)) { urls.remove(url); + // TODO: 2018/4/4 有问题 DownloadTaskEntity det = DbEntity.findFirst(DownloadTaskEntity.class, "url=? and isGroupTask='true'", url); if (det != null) { @@ -273,7 +274,7 @@ public abstract class AbsGroupUtil implements IUtil { dt.cancel(); } } - delDownloadInfo(); + CommonUtil.delDownloadGroupTaskConfig(mGTEntity.removeFile, mGTEntity.getEntity()); mGTEntity.deleteData(); } @@ -281,30 +282,6 @@ public abstract class AbsGroupUtil implements IUtil { } - /** - * 删除所有子任务的下载信息 - */ - private void delDownloadInfo() { - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mGTEntity.key); - if (tasks != null && !tasks.isEmpty()) { - for (DownloadTaskEntity taskEntity : tasks) { - CommonUtil.delDownloadTaskConfig(mGTEntity.removeFile, taskEntity); - } - } - - File dir = new File(mGTEntity.getEntity().getDirPath()); - if (mGTEntity.removeFile) { - if (dir.exists()) { - dir.delete(); - } - } else { - if (!mGTEntity.getEntity().isComplete()) { - dir.delete(); - } - } - } - @Override public void stop() { closeTimer(false); onStop(); @@ -418,7 +395,7 @@ public abstract class AbsGroupUtil implements IUtil { taskEntity.headers = mGTEntity.headers; taskEntity.requestEnum = mGTEntity.requestEnum; taskEntity.removeFile = mGTEntity.removeFile; - taskEntity.groupName = mGTEntity.key; + taskEntity.groupName = mGTEntity.getKey(); taskEntity.isGroupTask = true; taskEntity.requestType = mGTEntity.requestType; taskEntity.key = entity.getDownloadPath(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java index 719785c2..f2c9665d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java @@ -44,12 +44,12 @@ public class FtpDirDownloadUtil extends AbsGroupUtil { new FtpDirInfoThread(mGTEntity, new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { if (info.code >= 200 && info.code < 300) { - for (DownloadEntity entity : mGTEntity.entity.getSubTask()) { + for (DownloadEntity entity : mGTEntity.getEntity().getSubTask()) { mExeMap.put(entity.getUrl(), createChildDownloadTask(entity)); } - mActualTaskNum = mGTEntity.entity.getSubTask().size(); + mActualTaskNum = mGTEntity.getEntity().getSubTask().size(); mGroupSize = mActualTaskNum; - mTotalLen = mGTEntity.entity.getFileSize(); + mTotalLen = mGTEntity.getEntity().getFileSize(); startDownload(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java index 9e914437..8e385e82 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java @@ -160,7 +160,10 @@ class HttpFileInfoThread implements Runnable { } return; } - newUrl = CheckUtil.checkUrl(newUrl); + if (!CheckUtil.checkUrl(newUrl)) { + failDownload("下载失败,重定向url错误", false); + return; + } String cookies = conn.getHeaderField("Set-Cookie"); conn = (HttpURLConnection) new URL(newUrl).openConnection(); conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java index cfb731bc..aa297862 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java @@ -18,7 +18,7 @@ package com.arialyy.aria.core.inf; import android.os.Parcel; import android.os.Parcelable; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.Ignore; +import com.arialyy.aria.orm.annotation.Ignore; /** * Created by AriaL on 2017/6/29. diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java index 132b78f7..d8250ab7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java @@ -17,10 +17,7 @@ package com.arialyy.aria.core.inf; import android.os.Parcel; import android.os.Parcelable; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.orm.Foreign; -import com.arialyy.aria.orm.NormalList; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.annotation.Primary; import java.util.ArrayList; import java.util.List; @@ -29,20 +26,16 @@ import java.util.List; */ public abstract class AbsGroupEntity extends AbsEntity implements Parcelable { /** - * 组名,组名为任务地址相加的urlMd5 + * 组名,组名为任务地址相加的url的Md5 */ - @Primary @Foreign(table = DownloadGroupTaskEntity.class, column = "key") protected String - groupName = ""; + @Primary protected String groupName = ""; /** * 任务组别名 */ private String alias = ""; - /** - * 子任务链接组 - */ - @NormalList(clazz = String.class) private List urls = new ArrayList<>(); + private List urls = new ArrayList<>(); public List getUrls() { return urls; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java index 5f03f29a..c6391ae2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java @@ -17,8 +17,6 @@ package com.arialyy.aria.core.inf; import android.os.Parcel; import android.os.Parcelable; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.Ignore; /** * Created by AriaL on 2017/6/3. diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java index 742f4af6..eac0d191 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java @@ -34,17 +34,25 @@ import java.util.List; */ public abstract class AbsTarget implements ITarget { + protected String TAG = ""; protected ENTITY mEntity; protected TASK_ENTITY mTaskEntity; protected String mTargetName; + protected AbsTarget() { + TAG = CommonUtil.getClassName(this); + } + /** - * 重置下载状态,将任务状态设置为未开始状态 + * 重置状态,将任务状态设置为未开始状态 */ public TARGET resetState() { - mTaskEntity.getEntity().setState(IEntity.STATE_WAIT); - mTaskEntity.refreshInfo = true; - mTaskEntity.update(); + if (checkEntity()) { + mTaskEntity.getEntity().setState(IEntity.STATE_WAIT); + mTaskEntity.refreshInfo = true; + } else { + ALog.e(TAG, "重置状态失败"); + } return (TARGET) this; } @@ -96,10 +104,16 @@ public abstract class AbsTarget extends DbEntity { */ public static final int U_FTP = 0xA2; - /** - * Task实体对应的key - */ - @Primary public String key = ""; - /** * 账号和密码 */ @@ -144,12 +138,7 @@ public abstract class AbsTaskEntity extends DbEntity { return getEntity().getState(); } - @Override public void deleteData() { - if (getEntity() != null) { - getEntity().deleteData(); - } - super.deleteData(); - } + public abstract String getKey(); @Override public void update() { if (getEntity() != null) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java index 156a01ce..8ad7c7d1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.inf; -import com.arialyy.aria.orm.Ignore; +import com.arialyy.aria.orm.annotation.Ignore; /** * Created by lyy on 2017/2/23. diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java index 3387c49d..e6196375 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java @@ -15,8 +15,6 @@ */ package com.arialyy.aria.core.inf; -import com.arialyy.aria.core.inf.IEventListener; - /** * Created by lyy on 2017/2/9. * 上传监听 diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java index f68d7bad..af0ebce2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java @@ -18,6 +18,8 @@ package com.arialyy.aria.core.manager; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskWrapper; +import com.arialyy.aria.core.download.DownloadGroupWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CommonUtil; import java.util.List; @@ -46,36 +48,56 @@ class DGTEntityFactory implements ITEntityFactory wrapper = + DbEntity.findRelationData(DownloadGroupTaskWrapper.class, "DownloadGroupTaskWrapper.key=?", + entity.getGroupName()); + DownloadGroupTaskEntity dgTaskEntity; + + if (wrapper != null && !wrapper.isEmpty()) { + dgTaskEntity = wrapper.get(0).taskEntity; + if (dgTaskEntity == null) { + dgTaskEntity = new DownloadGroupTaskEntity(); + dgTaskEntity.setEntity(entity); + } else if (dgTaskEntity.getEntity() == null || TextUtils.isEmpty( + dgTaskEntity.getEntity().getKey())) { + dgTaskEntity.setEntity(entity); + } + } else { dgTaskEntity = new DownloadGroupTaskEntity(); - dgTaskEntity.save(entity); - } - if (dgTaskEntity.entity == null || TextUtils.isEmpty(dgTaskEntity.entity.getKey())) { - dgTaskEntity.save(entity); + dgTaskEntity.setEntity(entity); } + return dgTaskEntity; } /** - * 对于任务组,不能使用这个,可用于FTP文件夹下载 + * 对于任务组,不能使用这个,该方法只用于FTP文件夹下载 * - * @deprecated {@link #create(String, List)} + * @deprecated 任务组使用:{@link #create(String, List)} */ @Override @Deprecated public DownloadGroupTaskEntity create(String key) { - DownloadGroupTaskEntity dgTaskEntity = - DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", key); - if (dgTaskEntity == null) { + List wrapper = + DbEntity.findRelationData(DownloadGroupTaskWrapper.class, "DownloadGroupTaskWrapper.key=?", + key); + DownloadGroupTaskEntity dgTaskEntity; + if (wrapper != null && !wrapper.isEmpty()) { + dgTaskEntity = wrapper.get(0).taskEntity; + if (dgTaskEntity == null) { + dgTaskEntity = new DownloadGroupTaskEntity(); + dgTaskEntity.setEntity(getDownloadGroupEntity(key, null)); + } else if (dgTaskEntity.getEntity() == null || TextUtils.isEmpty( + dgTaskEntity.getEntity().getKey())) { + dgTaskEntity.setEntity(getDownloadGroupEntity(key, null)); + } + dgTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(key); + return dgTaskEntity; + } else { dgTaskEntity = new DownloadGroupTaskEntity(); - dgTaskEntity.save(getDownloadGroupEntity(key, null)); + dgTaskEntity.setEntity(getDownloadGroupEntity(key, null)); + dgTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(key); + return dgTaskEntity; } - if (dgTaskEntity.entity == null || TextUtils.isEmpty(dgTaskEntity.entity.getKey())) { - dgTaskEntity.save(getDownloadGroupEntity(key, null)); - } - dgTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(key); - return dgTaskEntity; } @Override public DownloadGroupTaskEntity create(String groupName, List urls) { @@ -86,13 +108,21 @@ class DGTEntityFactory implements ITEntityFactory urls) { - DownloadGroupEntity entity = - DbEntity.findFirst(DownloadGroupEntity.class, "groupName=?", groupName); - if (entity == null) { - entity = new DownloadGroupEntity(); - entity.setGroupName(groupName); - entity.setUrls(urls); + + List wrapper = + DbEntity.findRelationData(DownloadGroupWrapper.class, "DownloadGroupEntity.groupName=?", + groupName); + DownloadGroupEntity groupEntity; + if (wrapper != null && !wrapper.isEmpty()) { + groupEntity = wrapper.get(0).groupEntity; + if (groupEntity == null) { + groupEntity = new DownloadGroupEntity(); + } + } else { + groupEntity = new DownloadGroupEntity(); } - return entity; + groupEntity.setGroupName(groupName); + groupEntity.setUrls(urls); + return groupEntity; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java index 437b4171..6ec6eb63 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java @@ -18,9 +18,11 @@ package com.arialyy.aria.core.manager; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.orm.DbEntity; import java.io.File; +import java.util.List; /** * Created by Aria.Lao on 2017/11/1. @@ -45,19 +47,26 @@ class DTEntityFactory implements ITEntityFactory wrapper = DbEntity.findRelationData(DownloadTaskWrapper.class, + "DownloadTaskEntity.key=? and DownloadTaskEntity.isGroupTask='false' and DownloadTaskEntity.url=?", + entity.getDownloadPath(), entity.getUrl()); + + if (wrapper != null && !wrapper.isEmpty()) { + DownloadTaskEntity taskEntity = wrapper.get(0).taskEntity; + if (taskEntity == null) { + taskEntity = new DownloadTaskEntity(); + taskEntity.entity = entity; + } else if (taskEntity.entity == null || TextUtils.isEmpty(taskEntity.entity.getUrl())) { + taskEntity.entity = entity; + } + + return taskEntity; + } else { + DownloadTaskEntity taskEntity = new DownloadTaskEntity(); + taskEntity.entity = entity; + return taskEntity; } - return taskEntity; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java index d92f7f2c..d5d2b97d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java @@ -22,10 +22,6 @@ import com.arialyy.aria.core.inf.AbsTaskEntity; * Created by Aria.Lao on 2017/11/1. */ interface ITEntityFactory> { - /** - * 通过信息实体创建任务实体 - */ - TASK_ENTITY create(ENTITY entity); /** * 通过key创建任务,只适应于单任务 diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java index 56018a88..3ed9f3c1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java @@ -18,7 +18,6 @@ package com.arialyy.aria.core.manager; import android.support.v4.util.LruCache; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.util.ALog; @@ -29,7 +28,7 @@ import java.util.concurrent.locks.ReentrantLock; /** * Created by Aria.Lao on 2017/11/1. - * 任务实体管理器,负责 + * 任务实体管理器 */ public class TEManager { private static final String TAG = "TaskManager"; @@ -102,31 +101,6 @@ public class TEManager { } } - /** - * 通过实体创建任务 - * - * @return 如果任务实体创建失败,返回null - */ - public TE createTEntity(Class clazz, AbsEntity absEntity) { - final Lock lock = this.lock; - lock.lock(); - try { - AbsTaskEntity tEntity = cache.get(convertKey(absEntity.getKey())); - if (tEntity == null || tEntity.getClass() != clazz) { - ITEntityFactory factory = chooseFactory(clazz); - if (factory == null) { - ALog.e(TAG, "任务实体创建失败"); - return null; - } - tEntity = factory.create(absEntity); - cache.put(convertKey(absEntity.getKey()), tEntity); - } - return (TE) tEntity; - } finally { - lock.unlock(); - } - } - private IGTEntityFactory chooseGroupFactory(Class clazz) { if (clazz == DownloadGroupTaskEntity.class) { return DGTEntityFactory.getInstance(); @@ -146,7 +120,9 @@ public class TEManager { } /** - * 从任务实体管理器中获取任务实体 + * 从缓存中获取任务实体,如果任务实体不存在,则创建任务实体 + * + * @return 创建失败,返回null */ public TE getTEntity(Class clazz, String key) { final Lock lock = this.lock; @@ -154,7 +130,33 @@ public class TEManager { try { AbsTaskEntity tEntity = cache.get(convertKey(key)); if (tEntity == null) { - return null; + return createTEntity(clazz, key); + } else { + return (TE) tEntity; + } + } finally { + lock.unlock(); + } + } + + /** + * 从缓存中获取HTTP任务组的任务实体,如果任务实体不存在,则创建任务实体 + * + * @param urls HTTP任务组的子任务下载地址列表 + * @return 地址列表为null或创建实体失败,返回null + */ + public TE getGTEntity(Class clazz, List urls) { + if (urls == null || urls.isEmpty()) { + ALog.e(TAG, "获取HTTP任务组实体失败:任务组的子任务下载地址列表为null"); + return null; + } + final Lock lock = this.lock; + lock.lock(); + try { + String groupName = CommonUtil.getMd5Code(urls); + AbsTaskEntity tEntity = cache.get(convertKey(groupName)); + if (tEntity == null) { + return createGTEntity(clazz, urls); } else { return (TE) tEntity; } @@ -189,7 +191,7 @@ public class TEManager { final Lock lock = this.lock; lock.lock(); try { - return cache.put(convertKey(te.key), te) != null; + return cache.put(convertKey(te.getKey()), te) != null; } finally { lock.unlock(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java deleted file mode 100644 index e0f17a0b..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.manager; - -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Created by Aria.Lao on 2017/9/1. - * 任务管理器 - */ -class TaskManager { - private static final String TAG = "TaskManager"; - private static volatile TaskManager INSTANCE = null; - private Map map = new ConcurrentHashMap<>(); - - public static TaskManager getInstance() { - if (INSTANCE == null) { - synchronized (TaskManager.class) { - INSTANCE = new TaskManager(); - } - } - return INSTANCE; - } - - private TaskManager() { - - } - - /** - * 管理器添加任务 - * - * @param key 任务的key,下载为保存路径,任务组为任务组名,上传为文件上传路径 - * @param task 任务 - * @return {@code true}添加成功 - */ - public boolean addTask(String key, Class clazz, T task) { - String hash = CommonUtil.keyToHashKey(key); - if (map.keySet().contains(hash)) { - ALog.e(TAG, "任务【" + key + "】已存在"); - return false; - } - map.put(CommonUtil.keyToHashKey(key), task); - return true; - } - - /** - * 移除任务 - * - * @param key 任务的key,下载为保存路径,任务组为任务组名,上传为文件上传路径 - */ - public void removeTask(String key) { - String hash = CommonUtil.keyToHashKey(key); - for (Iterator> iter = map.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = iter.next(); - if (entry.getKey().equals(hash)) iter.remove(); - } - } - - /** - * 通过key获取任务 - * - * @return 如果找不到任务,返回null,否则返回key对应的任务 - */ - public AbsTask getTask(String key) { - return map.get(CommonUtil.keyToHashKey(key)); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java index 629daa5d..fa2e8a60 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java @@ -15,10 +15,13 @@ */ package com.arialyy.aria.core.manager; +import android.text.TextUtils; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.core.upload.UploadTaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.Regular; +import java.util.List; import java.util.regex.Pattern; /** @@ -41,17 +44,26 @@ class UTEntityFactory implements ITEntityFactory return INSTANCE; } - @Override public UploadTaskEntity create(UploadEntity entity) { - UploadTaskEntity uTaskEntity = - DbEntity.findFirst(UploadTaskEntity.class, "key=?", entity.getFilePath()); - if (uTaskEntity == null) { - uTaskEntity = new UploadTaskEntity(); - uTaskEntity.entity = entity; - } - if (uTaskEntity.entity == null) { + private UploadTaskEntity create(UploadEntity entity) { + List wrapper = + DbEntity.findRelationData(UploadTaskWrapper.class, "UploadTaskEntity.key=?", + entity.getFilePath()); + + if (wrapper != null && !wrapper.isEmpty()) { + UploadTaskEntity uTaskEntity = wrapper.get(0).taskEntity; + if (uTaskEntity == null) { + uTaskEntity = new UploadTaskEntity(); + uTaskEntity.entity = entity; + } else if (uTaskEntity.entity == null || TextUtils.isEmpty( + uTaskEntity.entity.getFilePath())) { + uTaskEntity.entity = entity; + } + return uTaskEntity; + } else { + UploadTaskEntity uTaskEntity = new UploadTaskEntity(); uTaskEntity.entity = entity; + return uTaskEntity; } - return uTaskEntity; } @Override public UploadTaskEntity create(String key) { @@ -67,14 +79,12 @@ class UTEntityFactory implements ITEntityFactory UploadEntity entity = UploadEntity.findFirst(UploadEntity.class, "filePath=?", filePath); if (entity == null) { entity = new UploadEntity(); - //String regex = "[/|\\\\|//]"; String regex = Regular.REG_FILE_NAME; Pattern p = Pattern.compile(regex); String[] strs = p.split(filePath); String fileName = strs[strs.length - 1]; entity.setFileName(fileName); entity.setFilePath(filePath); - entity.insert(); } return entity; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java index ad4b7c7d..421df17a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java @@ -67,7 +67,7 @@ public class DownloadGroupTaskQueue && mExecutePool.getTask(entity.getEntity().getKey()) == null) { task = (DownloadGroupTask) TaskFactory.getInstance() .createTask(targetName, entity, DownloadGroupSchedulers.getInstance()); - entity.key = entity.getEntity().getGroupName(); + entity.setKey(entity.getEntity().getGroupName()); mCachePool.putTask(task); } else { ALog.w(TAG, "任务已存在"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java index 4fba7b68..5a747bae 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java @@ -17,12 +17,9 @@ package com.arialyy.aria.core.upload; import android.support.annotation.NonNull; import android.text.TextUtils; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.queue.UploadTaskQueue; -import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; import java.io.File; /** @@ -30,13 +27,9 @@ import java.io.File; */ abstract class BaseNormalTarget extends AbsUploadTarget { - private static final String TAG = "BaseNormalTarget"; protected void initTarget(String filePath) { mTaskEntity = TEManager.getInstance().getTEntity(UploadTaskEntity.class, filePath); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(UploadTaskEntity.class, filePath); - } mEntity = mTaskEntity.entity; File file = new File(filePath); mEntity.setFileName(file.getName()); @@ -74,7 +67,11 @@ abstract class BaseNormalTarget } @Override protected boolean checkEntity() { - return checkUrl() && checkFilePath(); + boolean b = checkUrl() && checkFilePath(); + if (b) { + mTaskEntity.save(mEntity); + } + return b; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/FtpUploadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/FtpUploadTarget.java index 34922b45..8c48e213 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/FtpUploadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/FtpUploadTarget.java @@ -30,7 +30,6 @@ import com.arialyy.aria.util.CommonUtil; */ public class FtpUploadTarget extends BaseNormalTarget implements IFtpTarget { - private final String TAG = "FtpUploadTarget"; private FtpDelegate mDelegate; FtpUploadTarget(String filePath, String targetName) { @@ -51,14 +50,15 @@ public class FtpUploadTarget extends BaseNormalTarget */ @Override public FtpUploadTarget setUploadUrl(@NonNull String uploadUrl) { - uploadUrl = CheckUtil.checkUrl(uploadUrl); + if (!CheckUtil.checkUrl(uploadUrl)) { + return this; + } if (!uploadUrl.endsWith("/")) { uploadUrl += "/"; } mTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(uploadUrl); if (mEntity.getUrl().equals(uploadUrl)) return this; mEntity.setUrl(uploadUrl); - mEntity.update(); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java index 7c273459..9db2c3fa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java @@ -19,15 +19,18 @@ import android.os.Parcel; import android.os.Parcelable; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.Foreign; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Primary; /** * Created by lyy on 2017/2/9. * 上传文件实体 */ public class UploadEntity extends AbsNormalEntity implements Parcelable { - @Primary @Foreign(table = UploadTaskEntity.class, column = "key") private String filePath; //文件路径 + /** + * 文件上传路径 + */ + @Primary @Foreign(parent = UploadTaskEntity.class, column = "key") private String filePath; /** * 上传完成后服务器返回的数据 diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 3d9b6040..134d1b23 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -20,9 +20,7 @@ import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.common.ProxyHelper; -import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.scheduler.ISchedulerListener; import com.arialyy.aria.core.scheduler.UploadSchedulers; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CheckUtil; @@ -92,7 +90,7 @@ public class UploadReceiver extends AbsReceiver { @Override public void removeAllTask(boolean removeFile) { final AriaManager am = AriaManager.getInstance(AriaManager.APP); - am.setCmd(CommonUtil.createNormalCmd(targetName, new DownloadTaskEntity(), + am.setCmd(CommonUtil.createNormalCmd(targetName, new UploadTaskEntity(), NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_UPLOAD)).exe(); Set keys = am.getReceiver().keySet(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java index a7b4a042..ba4c3457 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java @@ -29,7 +29,6 @@ import java.util.Map; */ public class UploadTarget extends BaseNormalTarget implements IHttpHeaderTarget { - private static final String TAG = "UploadTarget"; private HttpHeaderDelegate mDelegate; UploadTarget(String filePath, String targetName) { @@ -47,12 +46,13 @@ public class UploadTarget extends BaseNormalTarget } @Override public UploadTarget setUploadUrl(@NonNull String uploadUrl) { - uploadUrl = CheckUtil.checkUrl(uploadUrl); + if (!CheckUtil.checkUrl(uploadUrl)){ + return this; + } if (mEntity.getUrl().equals(uploadUrl)) { return this; } mEntity.setUrl(uploadUrl); - mEntity.update(); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskWrapper.java new file mode 100644 index 00000000..3eea9148 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskWrapper.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.upload; + +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + */ +@Wrapper +public class UploadTaskWrapper extends AbsWrapper { + + @One + public UploadEntity entity; + + @Many(parentColumn = "filePath", entityColumn = "key") + private List taskEntitys = null; + + public UploadTaskEntity taskEntity; + + @Override public void handleConvert() { + //taskEntity.entity = (tEntity == null || tEntity.isEmpty()) ? null : tEntity.get(0); + taskEntity = (taskEntitys == null || taskEntitys.isEmpty()) ? null : taskEntitys.get(0); + if (taskEntity != null) { + taskEntity.entity = entity; + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java b/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java new file mode 100644 index 00000000..e3a1ae64 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.support.v4.util.LruCache; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.net.URLEncoder; + +/** + * Created by laoyuyu on 2018/3/22. + */ +abstract class AbsDelegate { + static final String TAG = "AbsDelegate"; + static final int CREATE_TABLE = 0; + static final int TABLE_EXISTS = 1; + static final int INSERT_DATA = 2; + static final int MODIFY_DATA = 3; + static final int FIND_DATA = 4; + static final int FIND_ALL_DATA = 5; + static final int DEL_DATA = 6; + static final int ROW_ID = 7; + static final int RELATION = 8; + static final int DROP_TABLE = 9; + + static LruCache mDataCache = new LruCache<>(1024); + + /** + * 打印数据库日志 + * + * @param type {@link DelegateWrapper} + */ + static void print(int type, String sql) { + if (!ALog.DEBUG) { + return; + } + String str = ""; + switch (type) { + case CREATE_TABLE: + str = "创建表 >>>> "; + break; + case TABLE_EXISTS: + str = "表是否存在 >>>> "; + break; + case INSERT_DATA: + str = "插入数据 >>>> "; + break; + case MODIFY_DATA: + str = "修改数据 >>>> "; + break; + case FIND_DATA: + str = "查询一行数据 >>>> "; + break; + case FIND_ALL_DATA: + str = "遍历整个数据库 >>>> "; + break; + case ROW_ID: + str = "查询RowId >>> "; + break; + case RELATION: + str = "查询关联表 >>> "; + break; + case DROP_TABLE: + str = "删除表 >>> "; + break; + } + ALog.d(TAG, str.concat(sql)); + } + + String getCacheKey(DbEntity dbEntity) { + return dbEntity.getClass().getName() + "_" + dbEntity.rowID; + } + + /** + * 检查list参数是否合法,list只能是{@code List} + * + * @return {@code true} 合法 + */ + boolean checkList(Field list) { + Class t = CommonUtil.getListParamType(list); + if (t != null && t == String.class) { + return true; + } else { + ALog.d(TAG, "map参数错误,支持List的参数字段"); + return false; + } + } + + /** + * 检查map参数是否合法,map只能是{@code Map} + * + * @return {@code true} 合法 + */ + boolean checkMap(Field map) { + Class[] ts = CommonUtil.getMapParamType(map); + if (ts != null + && ts[0] != null + && ts[1] != null + && ts[0] == String.class + && ts[1] == String.class) { + return true; + } else { + ALog.d(TAG, "map参数错误,支持Map的参数字段"); + return false; + } + } + + /** + * 为了防止特殊字符串导致存储失败,需要使用URL编码保存的字符串 + * + * @param value 需要保存的内容 + * @return 转换后的内容 + */ + String convertValue(String value) { + if (value.contains("'")) { + return URLEncoder.encode(value); + } + return value; + } + + void closeCursor(Cursor cursor) { + if (cursor != null && !cursor.isClosed()) { + try { + cursor.close(); + } catch (android.database.SQLException e) { + e.printStackTrace(); + } + } + } + + void close(SQLiteDatabase db) { + //if (db != null && db.isOpen()) db.close(); + } + + /** + * 检查数据库是否关闭,已经关闭的话,打开数据库 + * + * @return 返回数据库 + */ + SQLiteDatabase checkDb(SQLiteDatabase db) { + if (db == null || !db.isOpen()) { + db = SqlHelper.INSTANCE.getWritableDatabase(); + } + return db; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/UrlMapping.java b/Aria/src/main/java/com/arialyy/aria/orm/AbsWrapper.java similarity index 71% rename from Aria/src/main/java/com/arialyy/aria/core/UrlMapping.java rename to Aria/src/main/java/com/arialyy/aria/orm/AbsWrapper.java index 41098241..49f4eca1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/UrlMapping.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/AbsWrapper.java @@ -13,22 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core; - -import com.arialyy.aria.orm.DbEntity; +package com.arialyy.aria.orm; /** - * Created by lyy on 2017/10/17. - * Url 映射表 + * Created by laoyuyu on 2018/3/30. */ -public class UrlMapping extends DbEntity { - /** - * 各实体对应的key - */ - public String key; +public abstract class AbsWrapper { /** - * 链接 + * 处理转换 */ - public String url; + protected abstract void handleConvert(); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java b/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java new file mode 100644 index 00000000..614a35dc --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import com.arialyy.aria.orm.annotation.Foreign; + +/** + * Created by laoyuyu on 2018/3/22. + * on update 或 on delete 都可跟不同action功能 + * + * @see + * {@link Foreign#onDelete()}、{@link Foreign#onUpdate()} + */ +public enum ActionPolicy { + + /** + * 如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作 + */ + NO_ACTION("NO ACTION"), + + /** + * 和NO ACTION 作用一致,和NO ACTION的区别是: + * 主表update/delete执行时,马上就触发约束; + * 而NO ACTION 是执行完成语句后才触发约束, + */ + RESTRICT("RESTRICT"), + + /** + * 在父表上update/delete记录时,将子表上匹配记录的列设为null (要注意子表的外键列不能为not null) + */ + SET_NULL("SET NULL"), + + /** + * 父表有变更时,子表将外键列设置成一个默认的值,default配置的值 + */ + SET_DEFAULT("SET DEFAULT"), + + /** + * 在父表上update/delete记录时,同步update/delete掉子表的匹配记录 + */ + CASCADE("CASCADE"); + + String function; + + ActionPolicy(String function) { + this.function = function; + } + +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java index 3075d1d4..84113515 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -17,7 +17,6 @@ package com.arialyy.aria.orm; import android.text.TextUtils; import com.arialyy.aria.core.ErrorEntity; -import com.arialyy.aria.core.UrlMapping; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; @@ -34,7 +33,7 @@ import java.util.Map; class DBConfig { static Map mapping = new HashMap<>(); static String DB_NAME; - static int VERSION = 25; + static int VERSION = 31; static { if (TextUtils.isEmpty(DB_NAME)) { @@ -46,13 +45,12 @@ class DBConfig { } static { - mapping.put("DownloadEntity", DownloadEntity.class); + mapping.put("DownloadGroupTaskEntity", DownloadGroupTaskEntity.class); mapping.put("DownloadGroupEntity", DownloadGroupEntity.class); mapping.put("DownloadTaskEntity", DownloadTaskEntity.class); - mapping.put("DownloadGroupTaskEntity", DownloadGroupTaskEntity.class); - mapping.put("UploadEntity", UploadEntity.class); mapping.put("UploadTaskEntity", UploadTaskEntity.class); + mapping.put("DownloadEntity", DownloadEntity.class); + mapping.put("UploadEntity", UploadEntity.class); mapping.put("ErrorEntity", ErrorEntity.class); - //mapping.put("UrlMapping", UrlMapping.class); } } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java b/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java new file mode 100644 index 00000000..e5aa3292 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.content.Context; +import android.content.ContextWrapper; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.os.Environment; +import java.io.File; +import java.io.IOException; + +/** + * 保存在sd卡的数据库使用的Context + */ +class DatabaseContext extends ContextWrapper { + public DatabaseContext(Context context) { + super(context); + } + + /** + * 获得数据库路径,如果不存在,则创建对象对象 + */ + @Override + public File getDatabasePath(String name) { + //判断是否存在sd卡 + boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals( + android.os.Environment.getExternalStorageState()); + if (!sdExist) { + return null; + } else { + //获取sd卡路径 + File file = getBaseContext().getExternalFilesDir(null); + String dbDir; + if (file != null) { + dbDir = file.getPath() + "/"; + } else { + dbDir = Environment.getExternalStorageDirectory().getPath() + + "/Android/data/" + + getBaseContext().getPackageName() + + "/files/"; + } + + dbDir += "DB";//数据库所在目录 + String dbPath = dbDir + "/" + name;//数据库路径 + //判断目录是否存在,不存在则创建该目录 + File dirFile = new File(dbDir); + if (!dirFile.exists()) { + dirFile.mkdirs(); + } + + //数据库文件是否创建成功 + boolean isFileCreateSuccess = false; + //判断文件是否存在,不存在则创建该文件 + File dbFile = new File(dbPath); + if (!dbFile.exists()) { + try { + isFileCreateSuccess = dbFile.createNewFile();//创建文件 + } catch (IOException e) { + e.printStackTrace(); + } + } else { + isFileCreateSuccess = true; + } + + //返回数据库文件对象 + if (isFileCreateSuccess) { + return dbFile; + } else { + return null; + } + } + } + + /** + * 重载这个方法,是用来打开SD卡上的数据库的,android 2.3及以下会调用这个方法。 + */ + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, + SQLiteDatabase.CursorFactory factory) { + return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null); + } + + /** + * Android 4.0会调用此方法获取数据库。 + * + * @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String, int, + * android.database.sqlite.SQLiteDatabase.CursorFactory, + * android.database.DatabaseErrorHandler) + */ + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, + SQLiteDatabase.CursorFactory factory, + DatabaseErrorHandler errorHandler) { + return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null); + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java index 8b6ced31..56e09f3e 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java @@ -28,7 +28,7 @@ import java.util.Map; * Created by lyy on 2015/11/2. * 所有数据库实体父类 */ -public class DbEntity { +public abstract class DbEntity { private static final Object LOCK = new Object(); protected int rowID = -1; @@ -36,15 +36,14 @@ public class DbEntity { } - ///** - // * 关键字模糊检索全文 - // * - // * @param column 需要查找的列 - // * @param mathSql 关键字语法,exsimple “white OR green”、“blue AND red”、“white NOT green” - // */ - //public static List searchData(Class clazz, String column, String mathSql) { - // return DbUtil.getInstance().searchData(clazz, column, mathSql); - //} + /** + * 查询关联数据 + * + * @param expression 查询条件 + */ + public static List findRelationData(Class clazz, String... expression) { + return DelegateWrapper.getInstance().findRelationData(clazz, expression); + } /** * 检查某个字段的值是否存在 @@ -53,21 +52,21 @@ public class DbEntity { * @return {@code true}该字段的对应的value已存在 */ public static boolean checkDataExist(Class clazz, String... expression) { - return DbUtil.getInstance().checkDataExist(clazz, expression); + return DelegateWrapper.getInstance().checkDataExist(clazz, expression); } /** * 清空表数据 */ public static void clean(Class clazz) { - DbUtil.getInstance().clean(clazz); + DelegateWrapper.getInstance().clean(clazz); } /** * 直接执行sql语句 */ public static void exeSql(String sql) { - DbUtil.getInstance().exeSql(sql); + DelegateWrapper.getInstance().exeSql(sql); } /** @@ -76,7 +75,7 @@ public class DbEntity { * @return 没有数据返回null */ public static List findAllData(Class clazz) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); return util.findAllData(clazz); } @@ -97,7 +96,7 @@ public class DbEntity { * @return 没有数据返回null */ public static List findDatas(Class clazz, String... expression) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); return util.findData(clazz, expression); } @@ -110,7 +109,7 @@ public class DbEntity { * @return 没有数据返回null */ public static T findFirst(Class clazz, String... expression) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); List datas = util.findData(clazz, expression); return datas == null ? null : datas.size() > 0 ? datas.get(0) : null; } @@ -119,14 +118,14 @@ public class DbEntity { * 获取所有行的rowid */ public int[] getRowIds() { - return DbUtil.getInstance().getRowId(getClass()); + return DelegateWrapper.getInstance().getRowId(getClass()); } /** * 获取rowid */ public int getRowId(@NonNull Object[] wheres, @NonNull Object[] values) { - return DbUtil.getInstance().getRowId(getClass(), wheres, values); + return DelegateWrapper.getInstance().getRowId(getClass(), wheres, values); } /** @@ -143,7 +142,7 @@ public class DbEntity { * */ public static void deleteData(Class clazz, String... expression) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); util.delData(clazz, expression); } @@ -151,7 +150,7 @@ public class DbEntity { * 修改数据 */ public void update() { - DbUtil.getInstance().modifyData(this); + DelegateWrapper.getInstance().modifyData(this); } /** @@ -171,7 +170,7 @@ public class DbEntity { * 查找数据在表中是否存在 */ private boolean thisIsExist() { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); return util.isExist(getClass(), rowID); } @@ -179,45 +178,38 @@ public class DbEntity { * 插入数据 */ public void insert() { - DbUtil.getInstance().insertData(this); + DelegateWrapper.getInstance().insertData(this); updateRowID(); } - private T findFirst(Class clazz, @NonNull String[] wheres, - @NonNull String[] values) { - DbUtil util = DbUtil.getInstance(); - List list = util.findData(clazz, wheres, values); - return list == null ? null : list.get(0); - } - private void updateRowID() { try { List fields = CommonUtil.getAllFields(getClass()); - List where = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + List params = new ArrayList<>(); List values = new ArrayList<>(); for (Field field : fields) { field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { + if (SqlUtil.isIgnore(field)) { continue; } - where.add(field.getName()); + sb.append(field.getName()).append("=? AND "); Type type = field.getType(); - if (SqlUtil.isOneToOne(field)) { - values.add(SqlUtil.getOneToOneParams(field)); - } else if (type == List.class) { - if (SqlUtil.isOneToMany(field)) { - values.add(SqlUtil.getOneToManyElementParams(field)); - } else { - values.add(SqlUtil.list2Str(this, field)); - } + if (type == List.class) { + values.add(SqlUtil.list2Str(this, field)); } else if (type == Map.class) { values.add(SqlUtil.map2Str((Map) field.get(this))); } else { values.add(field.get(this) + ""); } } - DbEntity entity = findFirst(getClass(), where.toArray(new String[where.size()]), - values.toArray(new String[values.size()])); + + String temp = sb.toString(); + String p = temp.substring(0, temp.length() - 5); + + params.add(p); + params.addAll(values); + DbEntity entity = findFirst(getClass(), params.toArray(new String[params.size()])); if (entity != null) { rowID = entity.rowID; } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java deleted file mode 100644 index 7e1bf8d9..00000000 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.arialyy.aria.orm; - -import android.app.Application; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.NonNull; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; -import java.util.List; - -/** - * Created by lyy on 2015/2/11. - * 数据库操作工具 - */ -public class DbUtil { - private static final String TAG = "DbUtil"; - private volatile static DbUtil INSTANCE = null; - private int ROW_ID = 7; - private SQLiteDatabase mDb; - private SqlHelper mHelper; - - private DbUtil() { - - } - - private DbUtil(Context context) { - mHelper = SqlHelper.init(context.getApplicationContext()); - } - - public static DbUtil init(Context context) { - if (context instanceof Application) { - synchronized (AriaManager.LOCK) { - if (INSTANCE == null) { - INSTANCE = new DbUtil(context); - } - } - } - return INSTANCE; - } - - protected static DbUtil getInstance() { - if (INSTANCE == null) { - throw new NullPointerException("请在Application中调用init进行数据库工具注册注册"); - } - return INSTANCE; - } - - ///** - // * 关键字模糊检索全文 - // * - // * @param column 需要查找的列 - // * @param mathSql 关键字语法,exsimple “white OR green”、“blue AND red”、“white NOT green” - // */ - //public List searchData(Class clazz, String column, String mathSql) { - // checkDb(); - // return SqlHelper.searchData(mDb, clazz, column, mathSql); - //} - - /** - * 检查某个字段的值是否存在 - * - * @param expression 字段和值"url=xxx" - * @return {@code true}该字段的对应的value已存在 - */ - synchronized boolean checkDataExist(Class clazz, String... expression) { - checkDb(); - return SqlHelper.checkDataExist(mDb, clazz, expression); - } - - /** - * 清空表数据 - */ - synchronized void clean(Class clazz) { - checkDb(); - String tableName = CommonUtil.getClassName(clazz); - if (tableExists(clazz)) { - String sql = "DELETE FROM " + tableName; - exeSql(sql); - } - } - - /** - * 执行sql语句 - */ - void exeSql(String sql) { - mDb.execSQL(sql); - } - - /** - * 删除某条数据 - */ - synchronized void delData(Class clazz, String... expression) { - CheckUtil.checkSqlExpression(expression); - checkDb(); - SqlHelper.delData(mDb, clazz, expression); - } - - /** - * 修改某行数据 - */ - synchronized void modifyData(DbEntity dbEntity) { - checkDb(); - SqlHelper.modifyData(mDb, dbEntity); - } - - /** - * 遍历所有数据 - */ - synchronized List findAllData(Class clazz) { - checkDb(); - return SqlHelper.findAllData(mDb, clazz); - } - - /** - * 条件查寻数据 - */ - synchronized List findData(Class clazz, String... expression) { - checkDb(); - return SqlHelper.findData(mDb, clazz, expression); - } - - /** - * 通过rowId判断数据是否存在 - */ - synchronized boolean isExist(Class clazz, int rowId) { - checkDb(); - String sql = "SELECT rowid FROM " + CommonUtil.getClassName(clazz) + " WHERE rowid=" + rowId; - Cursor cursor = mDb.rawQuery(sql, null); - boolean isExist = cursor.getCount() > 0; - cursor.close(); - return isExist; - } - - /** - * 条件查寻数据 - */ - @Deprecated synchronized List findData(Class clazz, - @NonNull String[] wheres, @NonNull String[] values) { - checkDb(); - return SqlHelper.findData(mDb, clazz, wheres, values); - } - - /** - * 插入数据 - */ - synchronized void insertData(DbEntity dbEntity) { - checkDb(); - SqlHelper.insertData(mDb, dbEntity); - } - - /** - * 查找某张表是否存在 - */ - synchronized boolean tableExists(Class clazz) { - checkDb(); - return SqlHelper.tableExists(mDb, clazz); - } - - synchronized void createTable(Class clazz, String tableName) { - checkDb(); - SqlHelper.createTable(mDb, clazz, tableName); - } - - private void checkDb() { - if (mDb == null || !mDb.isOpen()) { - mDb = mHelper.getReadableDatabase(); - } - } - - /** - * 创建表 - */ - private synchronized void createTable(Class clazz) { - createTable(clazz, null); - } - - /** - * 关闭数据库 - */ - private synchronized void close() { - if (mDb != null) { - mDb.close(); - } - } - - /** - * 获取所在行Id - */ - synchronized int[] getRowId(Class clazz) { - checkDb(); - Cursor cursor = mDb.rawQuery("SELECT rowid, * FROM " + CommonUtil.getClassName(clazz), null); - int[] ids = new int[cursor.getCount()]; - int i = 0; - while (cursor.moveToNext()) { - ids[i] = cursor.getInt(cursor.getColumnIndex("rowid")); - i++; - } - cursor.close(); - close(); - return ids; - } - - /** - * 获取行Id - */ - synchronized int getRowId(Class clazz, Object[] wheres, Object[] values) { - checkDb(); - if (wheres.length <= 0 || values.length <= 0) { - ALog.e(TAG, "请输入删除条件"); - return -1; - } else if (wheres.length != values.length) { - ALog.e(TAG, "groupName 和 vaule 长度不相等"); - return -1; - } - StringBuilder sb = new StringBuilder(); - sb.append("SELECT rowid FROM ").append(CommonUtil.getClassName(clazz)).append(" WHERE "); - int i = 0; - for (Object where : wheres) { - sb.append(where).append("=").append("'").append(values[i]).append("'"); - sb.append(i >= wheres.length - 1 ? "" : ","); - i++; - } - SqlHelper.print(ROW_ID, sb.toString()); - Cursor c = mDb.rawQuery(sb.toString(), null); - int id = c.getColumnIndex("rowid"); - c.close(); - close(); - return id; - } -} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java new file mode 100644 index 00000000..c23fabf6 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Default; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Primary; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by laoyuyu on 2018/3/22. + * 通用委托,创建表,检查字段 + */ +class DelegateCommon extends AbsDelegate { + private DelegateCommon() { + } + + /** + * 删除指定的表 + */ + void dropTable(SQLiteDatabase db, String tableName) { + db = checkDb(db); + String deleteSQL = "DROP TABLE IF EXISTS ".concat(tableName); + print(DROP_TABLE, deleteSQL); + //db.beginTransaction(); + db.execSQL(deleteSQL); + //db.setTransactionSuccessful(); + //db.endTransaction(); + } + + /** + * 清空表数据 + */ + void clean(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + String tableName = CommonUtil.getClassName(clazz); + if (tableExists(db, clazz)) { + String sql = "DELETE FROM " + tableName; + db.execSQL(sql); + } + } + + /** + * 查找表是否存在 + * + * @param clazz 数据库实体 + * @return true,该数据库实体对应的表存在;false,不存在 + */ + boolean tableExists(SQLiteDatabase db, Class clazz) { + return tableExists(db, CommonUtil.getClassName(clazz)); + } + + private boolean tableExists(SQLiteDatabase db, String tableName) { + db = checkDb(db); + Cursor cursor = null; + try { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='"); + sb.append(tableName); + sb.append("'"); + print(TABLE_EXISTS, sb.toString()); + cursor = db.rawQuery(sb.toString(), null); + if (cursor != null && cursor.moveToNext()) { + int count = cursor.getInt(0); + if (count > 0) { + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + closeCursor(cursor); + close(db); + } + return false; + } + + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + String sql = + "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + expression[i + 1] + "'"; + } + sql = String.format(sql, params); + print(FIND_DATA, sql); + Cursor cursor = db.rawQuery(sql, null); + final boolean isExist = cursor.getCount() > 0; + closeCursor(cursor); + close(db); + return isExist; + } + + /** + * 创建表 + * + * @param clazz 数据库实体 + */ + void createTable(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + List fields = CommonUtil.getAllFields(clazz); + if (fields != null && fields.size() > 0) { + //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 + final List foreignArray = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE ") + .append(CommonUtil.getClassName(clazz)) + .append(" ("); + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + Class type = field.getType(); + sb.append(field.getName()); + if (type == String.class || type.isEnum()) { + sb.append(" VARCHAR"); + } else if (type == int.class || type == Integer.class) { + sb.append(" INTEGER"); + } else if (type == float.class || type == Float.class) { + sb.append(" FLOAT"); + } else if (type == double.class || type == Double.class) { + sb.append(" DOUBLE"); + } else if (type == long.class || type == Long.class) { + sb.append(" BIGINT"); + } else if (type == boolean.class || type == Boolean.class) { + sb.append(" BOOLEAN"); + } else if (type == java.util.Date.class || type == java.sql.Date.class) { + sb.append(" DATA"); + } else if (type == byte.class || type == Byte.class) { + sb.append(" BLOB"); + } else if (type == Map.class || type == List.class) { + sb.append(" TEXT"); + } else { + continue; + } + if (SqlUtil.isPrimary(field)) { + Primary pk = field.getAnnotation(Primary.class); + sb.append(" PRIMARY KEY"); + if (pk.autoincrement() && (type == int.class || type == Integer.class)) { + sb.append(" AUTOINCREMENT"); + } + } + + if (SqlUtil.isForeign(field)) { + foreignArray.add(field); + } + + if (SqlUtil.isNoNull(field)) { + sb.append(" NOT NULL"); + } + + if (SqlUtil.isDefault(field)) { + Default d = field.getAnnotation(Default.class); + if (!TextUtils.isEmpty(d.value())) { + sb.append(" DEFAULT ").append("'").append(d.value()).append("'"); + } + } + + sb.append(","); + } + + for (Field field : foreignArray) { + Foreign foreign = field.getAnnotation(Foreign.class); + sb.append("FOREIGN KEY (") + .append(field.getName()) + .append(") REFERENCES ") + .append(CommonUtil.getClassName(foreign.parent())) + .append("(") + .append(foreign.column()) + .append(")"); + ActionPolicy update = foreign.onUpdate(); + ActionPolicy delete = foreign.onDelete(); + if (update != ActionPolicy.NO_ACTION) { + sb.append(" ON UPDATE ").append(update.function); + } + + if (delete != ActionPolicy.NO_ACTION) { + sb.append(" ON DELETE ").append(update.function); + } + sb.append(","); + } + + String str = sb.toString(); + str = str.substring(0, str.length() - 1) + ");"; + print(CREATE_TABLE, str); + db.execSQL(str); + } + close(db); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java new file mode 100644 index 00000000..98a5866d --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.annotation.TargetApi; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Build; +import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * Created by laoyuyu on 2018/3/22. + * 查询数据 + */ +class DelegateFind extends AbsDelegate { + private final String PARENT_COLUMN_ALIAS = "p"; + private final String CHILD_COLUMN_ALIAS = "c"; + + private DelegateFind() { + } + + /** + * 查找一对多的关联数据 + * 如果查找不到数据或实体没有被{@link Wrapper}注解,将返回null + * 如果实体中没有{@link One}或{@link Many}注解,将返回null + * 如果实体中有多个{@link One}或{@link Many}注解,将返回nul + */ + List findRelationData(SQLiteDatabase db, Class clazz, + String... expression) { + db = checkDb(db); + + if (SqlUtil.isWrapper(clazz)) { + StringBuilder sb = new StringBuilder(); + Field[] fields = clazz.getDeclaredFields(); + Field one = null, many = null; + boolean hasOne = false, hasMany = false; + for (Field field : fields) { + if (SqlUtil.isOne(field)) { + if (hasOne) { + ALog.w(TAG, "查询数据失败,实体中有多个@One 注解"); + return null; + } + hasOne = true; + one = field; + } + if (SqlUtil.isMany(field)) { + if (hasMany) { + ALog.w(TAG, "查询数据失败,实体中有多个@Many 注解"); + return null; + } + hasMany = true; + many = field; + } + } + + if (one == null || many == null) { + ALog.w(TAG, "查询数据失败,实体中没有@One或@Many注解"); + return null; + } + + if (many.getType() != List.class) { + ALog.w(TAG, "查询数据失败,@Many注解的字段必须是List"); + return null; + } + try { + Many m = many.getAnnotation(Many.class); + Class parentClazz = Class.forName(one.getType().getName()); + Class childClazz = Class.forName(CommonUtil.getListParamType(many).getName()); + final String pTableName = parentClazz.getSimpleName(); + final String cTableName = childClazz.getSimpleName(); + List pColumn = SqlUtil.getAllNotIgnoreField(parentClazz); + List cColumn = SqlUtil.getAllNotIgnoreField(childClazz); + List pColumnAlias = new ArrayList<>(); + List cColumnAlias = new ArrayList<>(); + StringBuilder pSb = new StringBuilder(); + StringBuilder cSb = new StringBuilder(); + + if (pColumn != null) { + pSb.append(pTableName.concat(".rowid AS ").concat(PARENT_COLUMN_ALIAS).concat("rowid,")); + for (Field f : pColumn) { + String temp = PARENT_COLUMN_ALIAS.concat(f.getName()); + pColumnAlias.add(temp); + pSb.append(pTableName.concat(".").concat(f.getName())) + .append(" AS ") + .append(temp) + .append(","); + } + } + + if (cColumn != null) { + pSb.append(cTableName.concat(".rowid AS ").concat(CHILD_COLUMN_ALIAS).concat("rowid,")); + for (Field f : cColumn) { + String temp = CHILD_COLUMN_ALIAS.concat(f.getName()); + cColumnAlias.add(temp); + cSb.append(cTableName.concat(".").concat(f.getName())) + .append(" AS ") + .append(temp) + .append(","); + } + } + + String pColumnAlia = pSb.toString(); + String cColumnAlia = cSb.toString(); + if (!TextUtils.isEmpty(pColumnAlia)) { + pColumnAlia = pColumnAlia.substring(0, pColumnAlia.length() - 1); + } + + if (!TextUtils.isEmpty(cColumnAlia)) { + cColumnAlia = cColumnAlia.substring(0, cColumnAlia.length() - 1); + } + + sb.append("SELECT "); + + if (!TextUtils.isEmpty(pColumnAlia)) { + sb.append(pColumnAlia).append(","); + } + if (!TextUtils.isEmpty(cColumnAlia)) { + sb.append(cColumnAlia); + } + if (TextUtils.isEmpty(pColumnAlia) && TextUtils.isEmpty(cColumnAlia)) { + sb.append(" * "); + } + + sb.append(" FROM ") + .append(pTableName) + .append(" INNER JOIN ") + .append(cTableName) + .append(" ON ") + .append(pTableName.concat(".").concat(m.parentColumn())) + .append(" = ") + .append(cTableName.concat(".").concat(m.entityColumn())); + String sql; + if (expression != null && expression.length > 0) { + CheckUtil.checkSqlExpression(expression); + sb.append(" WHERE ").append(expression[0]).append(" "); + sql = sb.toString(); + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + convertValue(expression[i + 1]) + "'"; + } + sql = String.format(sql, params); + } else { + sql = sb.toString(); + } + print(RELATION, sql); + Cursor cursor = db.rawQuery(sql, null); + List data = + (List) newInstanceEntity(clazz, parentClazz, childClazz, cursor, pColumn, cColumn, + pColumnAlias, cColumnAlias); + closeCursor(cursor); + close(db); + return data; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } else { + ALog.w(TAG, "查询数据失败,实体类没有使用@Wrapper 注解"); + return null; + } + return null; + } + + /** + * 创建关联查询的数据 + * + * @param pColumn 父表的所有字段 + * @param cColumn 字表的所有字段 + * @param pColumnAlias 关联查询父表别名 + * @param cColumnAlias 关联查询子表别名 + */ + private List newInstanceEntity( + Class clazz, Class

parent, + Class child, + Cursor cursor, + List pColumn, List cColumn, + List pColumnAlias, List cColumnAlias) { + try { + String parentPrimary = ""; //父表主键别名 + for (Field f : pColumn) { + if (SqlUtil.isPrimary(f)) { + parentPrimary = PARENT_COLUMN_ALIAS.concat(f.getName()); + break; + } + } + + List wrappers = new ArrayList<>(); + Map tempParent = new WeakHashMap<>(); // 所有父表元素,key为父表主键的值 + Map> tempChild = new WeakHashMap<>(); // 所有的字表元素,key为父表主键的值 + + Object old = null; + while (cursor.moveToNext()) { + //创建父实体 + Object ppValue = setPPValue(parentPrimary, cursor); + if (old == null || ppValue != old) { //当主键不同时,表示是不同的父表数据 + old = ppValue; + if (tempParent.get(old) == null) { + P pEntity = parent.newInstance(); + String pPrimaryName = ""; + for (int i = 0, len = pColumnAlias.size(); i < len; i++) { + Field pField = pColumn.get(i); + pField.setAccessible(true); + Class type = pField.getType(); + int column = cursor.getColumnIndex(pColumnAlias.get(i)); + if (column == -1) continue; + setFieldValue(type, pField, column, cursor, pEntity); + + if (SqlUtil.isPrimary(pField) && (type == int.class || type == Integer.class)) { + pPrimaryName = pField.getName(); + } + } + + //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 + pEntity.rowID = cursor.getInt( + cursor.getColumnIndex( + TextUtils.isEmpty(pPrimaryName) ? PARENT_COLUMN_ALIAS.concat("rowid") + : pPrimaryName)); + + tempParent.put(ppValue, pEntity); + } + } + + // 创建子实体 + C cEntity = child.newInstance(); + String cPrimaryName = ""; + for (int i = 0, len = cColumnAlias.size(); i < len; i++) { + Field cField = cColumn.get(i); + cField.setAccessible(true); + Class type = cField.getType(); + + int column = cursor.getColumnIndex(cColumnAlias.get(i)); + if (column == -1) continue; + setFieldValue(type, cField, column, cursor, cEntity); + + if (SqlUtil.isPrimary(cField) && (type == int.class || type == Integer.class)) { + cPrimaryName = cField.getName(); + } + } + //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 + cEntity.rowID = cursor.getInt( + cursor.getColumnIndex( + TextUtils.isEmpty(cPrimaryName) ? CHILD_COLUMN_ALIAS.concat("rowid") + : cPrimaryName)); + if (tempChild.get(old) == null) { + tempChild.put(old, new ArrayList()); + } + tempChild.get(old).add(cEntity); + } + + List wFields = SqlUtil.getAllNotIgnoreField(clazz); + if (wFields != null && !wFields.isEmpty()) { + Set pKeys = tempParent.keySet(); + for (Object pk : pKeys) { + T wrapper = clazz.newInstance(); + P p = tempParent.get(pk); + boolean isPSet = false, isCSet = false; + for (Field f : wFields) { + if (!isPSet && f.getAnnotation(One.class) != null) { + f.set(wrapper, p); + isPSet = true; + } + if (!isCSet && f.getAnnotation(Many.class) != null) { + f.set(wrapper, tempChild.get(pk)); + isCSet = true; + } + } + wrapper.handleConvert(); //处理下转换 + wrappers.add(wrapper); + } + } + return wrappers; + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取父表主键数据 + * + * @param parentPrimary 父表主键别名 + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) private Object setPPValue(String parentPrimary, + Cursor cursor) { + Object ppValue = null; + int ppColumn = cursor.getColumnIndex(parentPrimary); //父表主键所在的列 + int type = cursor.getType(ppColumn); + switch (type) { + case Cursor.FIELD_TYPE_INTEGER: + ppValue = cursor.getLong(ppColumn); + break; + case Cursor.FIELD_TYPE_FLOAT: + ppValue = cursor.getFloat(ppColumn); + break; + case Cursor.FIELD_TYPE_STRING: + ppValue = cursor.getString(ppColumn); + break; + } + return ppValue; + } + + /** + * 条件查寻数据 + */ + List findData(SQLiteDatabase db, Class clazz, String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + String sql = + "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + convertValue(expression[i + 1]) + "'"; + } + sql = String.format(sql, params); + print(FIND_DATA, sql); + Cursor cursor = db.rawQuery(sql, null); + List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + closeCursor(cursor); + close(db); + return data; + } + + /** + * 查找表的所有数据 + */ + List findAllData(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT rowid, * FROM ").append(CommonUtil.getClassName(clazz)); + print(FIND_ALL_DATA, sb.toString()); + Cursor cursor = db.rawQuery(sb.toString(), null); + List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + closeCursor(cursor); + close(db); + return data; + } + + /** + * 根据数据游标创建一个具体的对象 + */ + private List newInstanceEntity(Class clazz, Cursor cursor) { + List fields = CommonUtil.getAllFields(clazz); + List entitys = new ArrayList<>(); + if (fields != null && fields.size() > 0) { + try { + while (cursor.moveToNext()) { + T entity = clazz.newInstance(); + String primaryName = ""; + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + + Class type = field.getType(); + if (SqlUtil.isPrimary(field) && (type == int.class || type == Integer.class)) { + primaryName = field.getName(); + } + + int column = cursor.getColumnIndex(field.getName()); + if (column == -1) continue; + setFieldValue(type, field, column, cursor, entity); + } + //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 + entity.rowID = cursor.getInt( + cursor.getColumnIndex(TextUtils.isEmpty(primaryName) ? "rowid" : primaryName)); + mDataCache.put(getCacheKey(entity), entity); + entitys.add(entity); + } + closeCursor(cursor); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + return entitys; + } + + /** + * 设置字段的值 + * + * @throws IllegalAccessException + */ + private void setFieldValue(Class type, Field field, int column, Cursor cursor, Object entity) + throws IllegalAccessException { + if (type == String.class) { + String temp = cursor.getString(column); + if (!TextUtils.isEmpty(temp)) { + field.set(entity, URLDecoder.decode(temp)); + } + } else if (type == int.class || type == Integer.class) { + field.setInt(entity, cursor.getInt(column)); + } else if (type == float.class || type == Float.class) { + field.setFloat(entity, cursor.getFloat(column)); + } else if (type == double.class || type == Double.class) { + field.setDouble(entity, cursor.getDouble(column)); + } else if (type == long.class || type == Long.class) { + field.setLong(entity, cursor.getLong(column)); + } else if (type == boolean.class || type == Boolean.class) { + String temp = cursor.getString(column); + if (TextUtils.isEmpty(temp)) { + field.setBoolean(entity, false); + } else { + field.setBoolean(entity, !temp.equalsIgnoreCase("false")); + } + } else if (type == java.util.Date.class || type == java.sql.Date.class) { + field.set(entity, new Date(cursor.getString(column))); + } else if (type == byte[].class) { + field.set(entity, cursor.getBlob(column)); + } else if (type == Map.class) { + String temp = cursor.getString(column); + if (!TextUtils.isEmpty(temp)) { + field.set(entity, SqlUtil.str2Map(temp)); + } + } else if (type == List.class) { + String value = cursor.getString(column); + if (!TextUtils.isEmpty(value)) { + field.set(entity, SqlUtil.str2List(value, field)); + } + } + } + + /** + * 获取所在行Id + */ + int[] getRowId(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + Cursor cursor = db.rawQuery("SELECT rowid, * FROM " + CommonUtil.getClassName(clazz), null); + int[] ids = new int[cursor.getCount()]; + int i = 0; + while (cursor.moveToNext()) { + ids[i] = cursor.getInt(cursor.getColumnIndex("rowid")); + i++; + } + cursor.close(); + close(db); + return ids; + } + + /** + * 获取行Id + */ + int getRowId(SQLiteDatabase db, Class clazz, Object[] wheres, Object[] values) { + db = checkDb(db); + if (wheres.length <= 0 || values.length <= 0) { + ALog.e(TAG, "请输入删除条件"); + return -1; + } else if (wheres.length != values.length) { + ALog.e(TAG, "groupName 和 vaule 长度不相等"); + return -1; + } + StringBuilder sb = new StringBuilder(); + sb.append("SELECT rowid FROM ").append(CommonUtil.getClassName(clazz)).append(" WHERE "); + int i = 0; + for (Object where : wheres) { + sb.append(where).append("=").append("'").append(values[i]).append("'"); + sb.append(i >= wheres.length - 1 ? "" : ","); + i++; + } + print(ROW_ID, sb.toString()); + Cursor c = db.rawQuery(sb.toString(), null); + int id = c.getColumnIndex("rowid"); + c.close(); + close(db); + return id; + } + + /** + * 通过rowId判断数据是否存在 + */ + boolean itemExist(SQLiteDatabase db, Class clazz, + int rowId) { + db = checkDb(db); + String sql = "SELECT rowid FROM " + CommonUtil.getClassName(clazz) + " WHERE rowid=" + rowId; + print(ROW_ID, sql); + Cursor cursor = db.rawQuery(sql, null); + boolean isExist = cursor.getCount() > 0; + cursor.close(); + return isExist; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java new file mode 100644 index 00000000..802a8bc6 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.util.SparseArray; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Created by laoyuyu on 2018/3/22. + * Delegate管理器 + */ +class DelegateManager { + private final String TAG = "ModuleFactory"; + + private SparseArray mDelegates = new SparseArray<>(); + private static volatile DelegateManager INSTANCE = null; + + private DelegateManager() { + + } + + static DelegateManager getInstance() { + if (INSTANCE == null) { + synchronized (DelegateManager.class) { + INSTANCE = new DelegateManager(); + } + } + return INSTANCE; + } + + /** + * 获取Module + */ + M getDelegate(Class clazz) { + M delegate = (M) mDelegates.get(clazz.hashCode()); + try { + if (delegate == null) { + Constructor c = clazz.getDeclaredConstructor(); + c.setAccessible(true); + delegate = (M) c.newInstance(); + mDelegates.put(clazz.hashCode(), delegate); + return delegate; + } + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return delegate; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java new file mode 100644 index 00000000..d697f5f0 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Primary; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +/** + * Created by laoyuyu on 2018/3/22. + * 增加数据、更新数据 + */ +class DelegateUpdate extends AbsDelegate { + private DelegateUpdate() { + } + + /** + * 删除某条数据 + */ + void delData(SQLiteDatabase db, Class clazz, String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + + String sql = "DELETE FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + expression[i + 1] + "'"; + } + sql = String.format(sql, params); + print(DEL_DATA, sql); + db.execSQL(sql); + close(db); + } + + /** + * 修改某行数据 + */ + void modifyData(SQLiteDatabase db, DbEntity dbEntity) { + db = checkDb(db); + Class clazz = dbEntity.getClass(); + List fields = CommonUtil.getAllFields(clazz); + DbEntity cacheEntity = mDataCache.get(getCacheKey(dbEntity)); + if (fields != null && fields.size() > 0) { + StringBuilder sql = new StringBuilder(); + StringBuilder prams = new StringBuilder(); + sql.append("UPDATE ").append(CommonUtil.getClassName(dbEntity)).append(" SET "); + int i = 0; + try { + for (Field field : fields) { + field.setAccessible(true); + if (isIgnore(dbEntity, field)) { + continue; + } + if (cacheEntity != null + && field.get(dbEntity) == field.get(cacheEntity) + && !field.getName().equals("state")) { //在LruCache中 state字段总是不能重新赋值... + continue; + } + + String value; + prams.append(i > 0 ? ", " : ""); + prams.append(field.getName()).append("='"); + Type type = field.getType(); + if (type == Map.class && checkMap(field)) { + value = SqlUtil.map2Str((Map) field.get(dbEntity)); + } else if (type == List.class && checkList(field)) { + value = SqlUtil.list2Str(dbEntity, field); + } else { + Object obj = field.get(dbEntity); + value = obj == null ? "" : convertValue(obj.toString()); + } + + prams.append(TextUtils.isEmpty(value) ? "" : value); + prams.append("'"); + i++; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + if (!TextUtils.isEmpty(prams.toString())) { + sql.append(prams.toString()); + sql.append(" where rowid=").append(dbEntity.rowID); + print(MODIFY_DATA, sql.toString()); + db.execSQL(sql.toString()); + } + } + mDataCache.put(getCacheKey(dbEntity), dbEntity); + close(db); + } + + /** + * 插入数据 + */ + void insertData(SQLiteDatabase db, DbEntity dbEntity) { + db = checkDb(db); + Class clazz = dbEntity.getClass(); + List fields = CommonUtil.getAllFields(clazz); + if (fields != null && fields.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("INSERT INTO ").append(CommonUtil.getClassName(dbEntity)).append("("); + int i = 0; + try { + for (Field field : fields) { + field.setAccessible(true); + if (isIgnore(dbEntity, field)) { + continue; + } + sb.append(i > 0 ? ", " : ""); + sb.append(field.getName()); + i++; + } + sb.append(") VALUES ("); + i = 0; + for (Field field : fields) { + field.setAccessible(true); + if (isIgnore(dbEntity, field)) { + continue; + } + sb.append(i > 0 ? ", " : ""); + sb.append("'"); + Type type = field.getType(); + if (type == Map.class && checkMap(field)) { + sb.append(SqlUtil.map2Str((Map) field.get(dbEntity))); + } else if (type == List.class && checkList(field)) { + sb.append(SqlUtil.list2Str(dbEntity, field)); + } else { + Object obj = field.get(dbEntity); + if (obj != null) { + sb.append(convertValue(field.get(dbEntity).toString())); + } + } + sb.append("'"); + i++; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + sb.append(")"); + print(INSERT_DATA, sb.toString()); + db.execSQL(sb.toString()); + } + close(db); + } + + /** + * {@code true}自动增长的主键和需要忽略的字段 + */ + private boolean isIgnore(Object obj, Field field) throws IllegalAccessException { + if (SqlUtil.isIgnore(field)) { + return true; + } + if (SqlUtil.isPrimary(field)) { //忽略自动增长的主键 + Primary p = field.getAnnotation(Primary.class); + if (p.autoincrement()) { + return true; + } + } + if (SqlUtil.isForeign(field)) { //忽略外键为null的字段 + Object value = field.get(obj); + if (value == null || (value instanceof String && TextUtils.isEmpty(String.valueOf(value)))) { + return true; + } + } + return false; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java new file mode 100644 index 00000000..ca33e472 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.aria.orm; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import java.util.List; + +/** + * Created by lyy on 2015/2/11. + * 数据库操作工具 + */ +public class DelegateWrapper { + private static final String TAG = "DelegateWrapper"; + private volatile static DelegateWrapper INSTANCE = null; + + private SQLiteDatabase mDb; + private DelegateManager mDManager; + + private DelegateWrapper() { + + } + + private DelegateWrapper(Context context) { + SqlHelper helper = SqlHelper.init(context.getApplicationContext()); + mDb = helper.getWritableDatabase(); + mDManager = DelegateManager.getInstance(); + } + + public static void init(Context context) { + synchronized (DelegateWrapper.class) { + if (INSTANCE == null) { + INSTANCE = new DelegateWrapper(context); + } + } + } + + static DelegateWrapper getInstance() { + if (INSTANCE == null) { + throw new NullPointerException("请在Application中调用init进行数据库工具注册注册"); + } + return INSTANCE; + } + + /** + * 查询关联表数据 + * + * @param expression 查询条件 + */ + List findRelationData(Class clazz, String... expression) { + return mDManager.getDelegate(DelegateFind.class).findRelationData(mDb, clazz, expression); + } + + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + boolean checkDataExist(Class clazz, String... expression) { + return mDManager.getDelegate(DelegateCommon.class) + .checkDataExist(mDb, clazz, expression); + } + + /** + * 清空表数据 + */ + void clean(Class clazz) { + mDManager.getDelegate(DelegateCommon.class).clean(mDb, clazz); + } + + /** + * 执行sql语句 + */ + void exeSql(String sql) { + mDb.execSQL(sql); + } + + /** + * 删除某条数据 + */ + void delData(Class clazz, String... expression) { + mDManager.getDelegate(DelegateUpdate.class).delData(mDb, clazz, expression); + } + + /** + * 修改某行数据 + */ + void modifyData(DbEntity dbEntity) { + mDManager.getDelegate(DelegateUpdate.class).modifyData(mDb, dbEntity); + } + + /** + * 遍历所有数据 + */ + List findAllData(Class clazz) { + return mDManager.getDelegate(DelegateFind.class).findAllData(mDb, clazz); + } + + /** + * 条件查寻数据 + */ + List findData(Class clazz, String... expression) { + return mDManager.getDelegate(DelegateFind.class).findData(mDb, clazz, expression); + } + + /** + * 通过rowId判断数据是否存在 + */ + boolean isExist(Class clazz, int rowId) { + return mDManager.getDelegate(DelegateFind.class).itemExist(mDb, clazz, rowId); + } + + /** + * 插入数据 + */ + void insertData(DbEntity dbEntity) { + mDManager.getDelegate(DelegateUpdate.class).insertData(mDb, dbEntity); + } + + /** + * 查找某张表是否存在 + */ + boolean tableExists(Class clazz) { + return mDManager.getDelegate(DelegateCommon.class).tableExists(mDb, clazz); + } + + /** + * 获取所在行Id + */ + int[] getRowId(Class clazz) { + return mDManager.getDelegate(DelegateFind.class).getRowId(mDb, clazz); + } + + /** + * 获取行Id + */ + int getRowId(Class clazz, Object[] wheres, Object[] values) { + return mDManager.getDelegate(DelegateFind.class).getRowId(mDb, clazz, wheres, values); + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java b/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java deleted file mode 100644 index 2d2ba512..00000000 --- a/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.orm; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Created by AriaL on 2017/7/4. - * 基本类型的List,只能用于常见的数据类型,如果是一对多的复杂数据结构,需要使用{@link OneToMany} - */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface NormalList { - /** - * 数据类型 - */ - Class clazz(); -} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 72be07c5..5231d09a 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -20,21 +20,11 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.v4.util.LruCache; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Date; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -43,31 +33,34 @@ import java.util.Set; */ final class SqlHelper extends SQLiteOpenHelper { private static final String TAG = "SqlHelper"; - private static final int CREATE_TABLE = 0; - private static final int TABLE_EXISTS = 1; - private static final int INSERT_DATA = 2; - private static final int MODIFY_DATA = 3; - private static final int FIND_DATA = 4; - private static final int FIND_ALL_DATA = 5; - private static final int DEL_DATA = 6; + /** + * 是否将数据库保存在Sd卡,{@code true} 是 + */ + private static final boolean SAVE_IN_SDCARD = false; + + static volatile SqlHelper INSTANCE = null; - private static volatile SqlHelper INSTANCE = null; - private static LruCache mDataCache = new LruCache<>(1024); - //private static Map mDataCache = new ConcurrentHashMap<>(); + private DelegateCommon mDelegate; static SqlHelper init(Context context) { if (INSTANCE == null) { - synchronized (AriaManager.LOCK) { - INSTANCE = new SqlHelper(context.getApplicationContext()); + synchronized (SqlHelper.class) { + DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); + INSTANCE = new SqlHelper(context.getApplicationContext(), delegate); SQLiteDatabase db = INSTANCE.getWritableDatabase(); - db = checkDb(db); + db = delegate.checkDb(db); + // SQLite在3.6.19版本中开始支持外键约束, + // 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22. + // 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能 + // 需要使用如下语句: + db.execSQL("PRAGMA foreign_keys=ON;"); Set tables = DBConfig.mapping.keySet(); for (String tableName : tables) { Class clazz = null; clazz = DBConfig.mapping.get(tableName); - if (!tableExists(db, clazz)) { - createTable(db, clazz, null); + if (!delegate.tableExists(db, clazz)) { + delegate.createTable(db, clazz); } } } @@ -75,8 +68,10 @@ final class SqlHelper extends SQLiteOpenHelper { return INSTANCE; } - private SqlHelper(Context context) { - super(context, DBConfig.DB_NAME, null, DBConfig.VERSION); + private SqlHelper(Context context, DelegateCommon delegate) { + super(SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null, + DBConfig.VERSION); + mDelegate = delegate; } @Override public void onCreate(SQLiteDatabase db) { @@ -85,7 +80,11 @@ final class SqlHelper extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < newVersion) { - handleDbUpdate(db); + if (oldVersion < 31) { + handle314AriaUpdate(db); + } else { + handleDbUpdate(db); + } } } @@ -110,635 +109,98 @@ final class SqlHelper extends SQLiteOpenHelper { Set tables = DBConfig.mapping.keySet(); for (String tableName : tables) { Class clazz = DBConfig.mapping.get(tableName); - if (tableExists(db, clazz)) { + if (mDelegate.tableExists(db, clazz)) { String countColumnSql = "SELECT rowid FROM " + tableName; Cursor cursor = db.rawQuery(countColumnSql, null); int dbColumnNum = cursor.getColumnCount(); - int newEntityColumnNum = getEntityAttr(clazz); + List fields = SqlUtil.getAllNotIgnoreField(clazz); + int newEntityColumnNum = (fields == null || fields.isEmpty()) ? 0 : fields.size(); if (dbColumnNum != newEntityColumnNum) { - back(db, clazz); - } - } - } - } - - /** - * 备份 - */ - private void back(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - String oldTableName = CommonUtil.getClassName(clazz); - //备份数据 - List list = findAllData(db, clazz); - //修改原来表名字 - String alertSql = "alter table " + oldTableName + " rename to " + oldTableName + "_temp"; - db.beginTransaction(); - db.execSQL(alertSql); - //创建一个原来新表 - createTable(db, clazz, null); - if (list != null && list.size() > 0) { - for (DbEntity entity : list) { - insertData(db, entity); - } - } - //删除原来的表 - String deleteSQL = "drop table IF EXISTS " + oldTableName + "_temp"; - db.execSQL(deleteSQL); - db.setTransactionSuccessful(); - db.endTransaction(); - close(db); - } - - /** - * 获取实体的字段数 - */ - private int getEntityAttr(Class clazz) { - int count = 1; - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - count++; - } - } - return count; - } - - ///** - // * 关键字模糊检索全文 - // * - // * @param column 需要查找的列 - // * @param mathSql 关键字语法,exsimple “white OR green”、“blue AND red”、“white NOT green” - // */ - //public static List searchData(SQLiteDatabase db, Class clazz, - // String column, String mathSql) { - // String sql = "SELECT * FROM " - // + CommonUtil.getClassName(clazz) - // + " WHERE " - // + column - // + " MATCH '" - // + mathSql - // + "'"; - // - // Cursor cursor = db.rawQuery(sql, null); - // List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - // closeCursor(cursor); - // return data; - //} - - /** - * 检查某个字段的值是否存在 - * - * @param expression 字段和值"url=xxx" - * @return {@code true}该字段的对应的value已存在 - */ - static synchronized boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - String sql = - "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = "'" + expression[i + 1] + "'"; - } - sql = String.format(sql, params); - print(FIND_DATA, sql); - Cursor cursor = db.rawQuery(sql, null); - final boolean isExist = cursor.getCount() > 0; - closeCursor(cursor); - close(db); - return isExist; - } - - /** - * 条件查寻数据 - */ - static synchronized List findData(SQLiteDatabase db, Class clazz, - String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - String sql = - "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = "'" + checkValue(expression[i + 1]) + "'"; - } - sql = String.format(sql, params); - print(FIND_DATA, sql); - Cursor cursor = db.rawQuery(sql, null); - List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - /** - * 条件查寻数据 - */ - static synchronized List findData(SQLiteDatabase db, String tableName, - String... expression) { - Class clazz = null; - try { - clazz = (Class) Class.forName(tableName); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - return findData(db, clazz, expression); - } - - /** - * 条件查寻数据 - */ - @Deprecated static synchronized List findData(SQLiteDatabase db, - Class clazz, @NonNull String[] wheres, @NonNull String[] values) { - db = checkDb(db); - if (wheres.length <= 0 || values.length <= 0) { - ALog.e(TAG, "请输入查询条件"); - return null; - } else if (wheres.length != values.length) { - ALog.e(TAG, "groupName 和 vaule 长度不相等"); - return null; - } - StringBuilder sb = new StringBuilder(); - sb.append("SELECT rowid, * FROM ").append(CommonUtil.getClassName(clazz)).append(" where "); - int i = 0; - for (Object where : wheres) { - sb.append(where).append("=").append("'").append(checkValue(values[i])).append("'"); - sb.append(i >= wheres.length - 1 ? "" : " AND "); - i++; - } - print(FIND_DATA, sb.toString()); - Cursor cursor = db.rawQuery(sb.toString(), null); - List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - private static String checkValue(String value) { - if (value.contains("'")) { - return URLEncoder.encode(value); - } - return value; - } - - /** - * 查找表的所有数据 - */ - static synchronized List findAllData(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - StringBuilder sb = new StringBuilder(); - sb.append("SELECT rowid, * FROM ").append(CommonUtil.getClassName(clazz)); - print(FIND_ALL_DATA, sb.toString()); - Cursor cursor = db.rawQuery(sb.toString(), null); - List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - /** - * 删除某条数据 - */ - static synchronized void delData(SQLiteDatabase db, Class clazz, - String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - - //List fields = CommonUtil.getAllFields(clazz); - //for (Field field : fields) { - // if (SqlUtil.isOneToOne(field)) { - // OneToOne oto = field.getAnnotation(OneToOne.class); - // delData(db, oto.table(), oto.key() + "=?", ); - // } else if (SqlUtil.isOneToMany(field)) { - // OneToMany otm = field.getAnnotation(OneToMany.class); - // delData(db, otm.table(), otm.key() + "=?", otm.key()); - // } - //} - - String sql = "DELETE FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = "'" + expression[i + 1] + "'"; - } - sql = String.format(sql, params); - SqlHelper.print(DEL_DATA, sql); - db.execSQL(sql); - close(db); - } - - /** - * 修改某行数据 - */ - static synchronized void modifyData(SQLiteDatabase db, DbEntity dbEntity) { - db = checkDb(db); - Class clazz = dbEntity.getClass(); - List fields = CommonUtil.getAllFields(clazz); - DbEntity cacheEntity = mDataCache.get(getCacheKey(dbEntity)); - if (fields != null && fields.size() > 0) { - StringBuilder sql = new StringBuilder(); - StringBuilder prams = new StringBuilder(); - sql.append("UPDATE ").append(CommonUtil.getClassName(dbEntity)).append(" SET "); - int i = 0; - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - try { - if (cacheEntity != null - && field.get(dbEntity) == field.get(cacheEntity) - && !field.getName().equals("state")) { //在LruCache中 state字段总是不能重新赋值... - //if (dbEntity instanceof DownloadEntity && field.getName().equals("state")) { - // Log.i(TAG, "cacheState => " - // + ((DownloadEntity) cacheEntity).getState() - // + ", newState => " - // + ((DownloadEntity) dbEntity).getState()); - //} - continue; - } - - //sb.append(i > 0 ? ", " : ""); - //sb.append(field.getName()).append("='"); - String value; - prams.append(i > 0 ? ", " : ""); - prams.append(field.getName()).append("='"); - Type type = field.getType(); - if (type == Map.class) { - value = SqlUtil.map2Str((Map) field.get(dbEntity)); - } else if (type == List.class) { - if (SqlUtil.isOneToMany(field)) { - value = SqlUtil.getOneToManyElementParams(field); - } else { - value = SqlUtil.list2Str(dbEntity, field); + db = mDelegate.checkDb(db); + //备份数据 + List list = + DelegateManager.getInstance().getDelegate(DelegateFind.class).findAllData(db, clazz); + //修改表名为中介表名 + String alertSql = "ALTER TABLE " + tableName + " RENAME TO " + tableName + "_temp"; + //db.beginTransaction(); + db.execSQL(alertSql); + + //创建一个原本的表 + mDelegate.createTable(db, clazz); + //传入原来的数据 + if (list != null && list.size() > 0) { + DelegateUpdate update = DelegateManager.getInstance().getDelegate(DelegateUpdate.class); + for (DbEntity entity : list) { + update.insertData(db, entity); } - } else if (SqlUtil.isOneToOne(field)) { - value = SqlUtil.getOneToOneParams(field); - } else { - Object obj = field.get(dbEntity); - value = obj == null ? "" : checkValue(obj.toString()); } - - //sb.append(value == null ? "" : value); - //sb.append("'"); - prams.append(TextUtils.isEmpty(value) ? "" : value); - prams.append("'"); - } catch (IllegalAccessException e) { - e.printStackTrace(); + //删除中介表 + mDelegate.dropTable(db, tableName + "_temp"); } - i++; - } - if (!TextUtils.isEmpty(prams.toString())) { - sql.append(prams.toString()); - sql.append(" where rowid=").append(dbEntity.rowID); - print(MODIFY_DATA, sql.toString()); - db.execSQL(sql.toString()); } } - mDataCache.put(getCacheKey(dbEntity), dbEntity); - close(db); - } - - private static String getCacheKey(DbEntity dbEntity) { - return dbEntity.getClass().getName() + "_" + dbEntity.rowID; + //db.setTransactionSuccessful(); + //db.endTransaction(); + mDelegate.close(db); } /** - * 插入数据 + * 处理3.4版本之前数据库迁移,主要是修改子表外键字段对应的值 */ - static synchronized void insertData(SQLiteDatabase db, DbEntity dbEntity) { - db = checkDb(db); - Class clazz = dbEntity.getClass(); - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - StringBuilder sb = new StringBuilder(); - sb.append("INSERT INTO ").append(CommonUtil.getClassName(dbEntity)).append("("); - int i = 0; - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - sb.append(i > 0 ? ", " : ""); - sb.append(field.getName()); - i++; - } - sb.append(") VALUES ("); - i = 0; - try { - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - sb.append(i > 0 ? ", " : ""); - sb.append("'"); - Type type = field.getType(); - if (type == Map.class) { - sb.append(SqlUtil.map2Str((Map) field.get(dbEntity))); - } else if (type == List.class) { - if (SqlUtil.isOneToMany(field)) { - sb.append(SqlUtil.getOneToManyElementParams(field)); - } else { - sb.append(SqlUtil.list2Str(dbEntity, field)); - } - } else if (SqlUtil.isOneToOne(field)) { - sb.append(SqlUtil.getOneToOneParams(field)); - } else { - sb.append(checkValue(field.get(dbEntity).toString())); - } - sb.append("'"); - i++; - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - sb.append(")"); - print(INSERT_DATA, sb.toString()); - db.execSQL(sb.toString()); - } - close(db); - } - - /** - * 查找表是否存在 - * - * @param clazz 数据库实体 - * @return true,该数据库实体对应的表存在;false,不存在 - */ - static synchronized boolean tableExists(SQLiteDatabase db, Class clazz) { - return tableExists(db, CommonUtil.getClassName(clazz)); - } - - static synchronized boolean tableExists(SQLiteDatabase db, String tableName) { - db = checkDb(db); - Cursor cursor = null; - try { - StringBuilder sb = new StringBuilder(); - sb.append("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='"); - sb.append(tableName); - sb.append("'"); - print(TABLE_EXISTS, sb.toString()); - cursor = db.rawQuery(sb.toString(), null); - if (cursor != null && cursor.moveToNext()) { - int count = cursor.getInt(0); - if (count > 0) { - return true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - closeCursor(cursor); - close(db); - } - return false; - } - - /** - * 创建表 - * - * @param clazz 数据库实体 - * @param tableName 数据库实体的类名 - */ - static synchronized void createTable(SQLiteDatabase db, Class clazz, String tableName) { - db = checkDb(db); - createFTSTable(db); - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 - final List foreignArray = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); - //sb.append("create VIRTUAL table ") - sb.append("create table ") - .append(TextUtils.isEmpty(tableName) ? CommonUtil.getClassName(clazz) : tableName) - //.append(" USING fts4("); - .append(" ("); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - Class type = field.getType(); - sb.append(field.getName()); - if (type == String.class || SqlUtil.isOneToOne(field) || type.isEnum()) { - sb.append(" varchar"); - } else if (type == int.class || type == Integer.class) { - sb.append(" interger"); - } else if (type == float.class || type == Float.class) { - sb.append(" float"); - } else if (type == double.class || type == Double.class) { - sb.append(" double"); - } else if (type == long.class || type == Long.class) { - sb.append(" bigint"); - } else if (type == boolean.class || type == Boolean.class) { - sb.append(" boolean"); - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - sb.append(" data"); - } else if (type == byte.class || type == Byte.class) { - sb.append(" blob"); - } else if (type == Map.class || type == List.class) { - sb.append(" text"); - } else { - continue; - } - if (SqlUtil.isPrimary(field)) { - //sb.append(" PRIMARY KEY"); - - } - if (SqlUtil.isForeign(field)) { - //foreignArray.add(field); - } - - if (SqlUtil.isNoNull(field)) { - sb.append(" NOT NULL"); - } - sb.append(","); - } + private void handle314AriaUpdate(SQLiteDatabase db) { + Set tables = DBConfig.mapping.keySet(); + for (String tableName : tables) { + Class clazz = DBConfig.mapping.get(tableName); - for (Field field : foreignArray) { - Foreign foreign = field.getAnnotation(Foreign.class); - sb.append("FOREIGN KEY (") - .append(field.getName()) - .append(") REFERENCES ") - .append(CommonUtil.getClassName(foreign.table())) - .append("(") - .append(foreign.column()) - .append("),"); + String pColumn = SqlUtil.getPrimaryName(clazz); + if (!TextUtils.isEmpty(pColumn)) { + //删除所有主键为null的数据 + String nullSql = + "DELETE FROM " + tableName + " WHERE " + pColumn + " = '' OR " + pColumn + " IS NULL"; + ALog.d(TAG, nullSql); + db.execSQL(nullSql); + + //删除所有主键重复的数据 + String repeatSql = "DELETE FROM " + + tableName + + " WHERE " + + pColumn + + " in (SELECT " + + pColumn + + " FROM " + + tableName + + " GROUP BY " + pColumn + " having count(" + pColumn + ") > 1)"; + + ALog.d(TAG, repeatSql); + db.execSQL(repeatSql); } - String str = sb.toString(); - str = str.substring(0, str.length() - 1) + ");"; - print(CREATE_TABLE, str); - db.execSQL(str); - } - close(db); - } - - /** - * 创建分词表 - */ - private static void createFTSTable(SQLiteDatabase db) { - String tableName = "ariaFts"; - String sql = "CREATE VIRTUAL TABLE " + tableName + " USING fts4(tokenize= porter)"; - if (!tableExists(db, tableName)) { - db.execSQL(sql); - } - } + //备份数据 + List list = + DelegateManager.getInstance().getDelegate(DelegateFind.class).findAllData(db, clazz); - /** - * 打印数据库日志 - * - * @param type {@link DbUtil} - */ - static void print(int type, String sql) { - if (true) { - return; - } - String str = ""; - switch (type) { - case CREATE_TABLE: - str = "创建表 >>>> "; - break; - case TABLE_EXISTS: - str = "表是否存在 >>>> "; - break; - case INSERT_DATA: - str = "插入数据 >>>> "; - break; - case MODIFY_DATA: - str = "修改数据 >>>> "; - break; - case FIND_DATA: - str = "查询一行数据 >>>> "; - break; - case FIND_ALL_DATA: - str = "遍历整个数据库 >>>> "; - break; - } - ALog.v(TAG, str + sql); - } + //修改表名为中介表名 + String alertSql = "ALTER TABLE " + tableName + " RENAME TO " + tableName + "_temp"; + db.execSQL(alertSql); - /** - * 根据数据游标创建一个具体的对象 - */ - private static synchronized List newInstanceEntity(SQLiteDatabase db, - Class clazz, Cursor cursor) { - List fields = CommonUtil.getAllFields(clazz); - List entitys = new ArrayList<>(); - if (fields != null && fields.size() > 0) { - try { - while (cursor.moveToNext()) { - T entity = clazz.newInstance(); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - Class type = field.getType(); - int column = cursor.getColumnIndex(field.getName()); - if (column == -1) continue; - if (type == String.class) { - field.set(entity, URLDecoder.decode(cursor.getString(column))); - } else if (type == int.class || type == Integer.class) { - field.setInt(entity, cursor.getInt(column)); - } else if (type == float.class || type == Float.class) { - field.setFloat(entity, cursor.getFloat(column)); - } else if (type == double.class || type == Double.class) { - field.setDouble(entity, cursor.getDouble(column)); - } else if (type == long.class || type == Long.class) { - field.setLong(entity, cursor.getLong(column)); - } else if (type == boolean.class || type == Boolean.class) { - field.setBoolean(entity, !cursor.getString(column).equalsIgnoreCase("false")); - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - field.set(entity, new Date(cursor.getString(column))); - } else if (type == byte[].class) { - field.set(entity, cursor.getBlob(column)); - } else if (type == Map.class) { - field.set(entity, SqlUtil.str2Map(cursor.getString(column))); - } else if (type == List.class) { - String value = cursor.getString(column); - if (SqlUtil.isOneToMany(field)) { - //主键字段 - String primaryKey = SqlUtil.getPrimaryName(clazz); - if (TextUtils.isEmpty(primaryKey)) { - throw new IllegalArgumentException("List中的元素对象必须需要@Primary注解的字段"); - } - //list字段保存的数据 - int kc = cursor.getColumnIndex(primaryKey); - String primaryData = cursor.getString(kc); - if (TextUtils.isEmpty(primaryData)) continue; - List list = findForeignData(db, primaryData, value); - if (list == null) continue; - field.set(entity, findForeignData(db, primaryData, value)); - } else { - field.set(entity, SqlUtil.str2List(value, field)); - } - } else if (SqlUtil.isOneToOne(field)) { - String primaryKey = SqlUtil.getPrimaryName(clazz); - if (TextUtils.isEmpty(primaryKey)) { - throw new IllegalArgumentException("@OneToOne的注解对象必须需要@Primary注解的字段"); - } - int kc = cursor.getColumnIndex(primaryKey); - String params = cursor.getString(column); - String primaryData = cursor.getString(kc); - if (TextUtils.isEmpty(primaryData) || primaryData.equalsIgnoreCase("null")) continue; - List list = findForeignData(db, primaryData, params); - if (list != null && list.size() > 0) { - field.set(entity, list.get(0)); - } - } + //创建一个原本的表 + mDelegate.createTable(db, clazz); + //插入数据 + if (list != null && list.size() > 0) { + DelegateUpdate update = DelegateManager.getInstance().getDelegate(DelegateUpdate.class); + try { + for (DbEntity entity : list) { + update.insertData(db, entity); } - entity.rowID = cursor.getInt(cursor.getColumnIndex("rowid")); - mDataCache.put(getCacheKey(entity), entity); - entitys.add(entity); + } catch (Exception e) { + ALog.e(TAG, CommonUtil.getPrintException(e)); } - closeCursor(cursor); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - return entitys; - } - - /** - * 查找一对多、一对一的关联数据 - * - * @param primary 当前表的主键 - * @param childParams 当前表关联数据的类名 $$ 主键名 - */ - private static List findForeignData(SQLiteDatabase db, String primary, - String childParams) { - String[] params = childParams.split("\\$\\$"); - return findData(db, params[0], params[1] + "=?", primary); - } - - private static void closeCursor(Cursor cursor) { - if (cursor != null && !cursor.isClosed()) { - try { - cursor.close(); - } catch (android.database.SQLException e) { - e.printStackTrace(); } - } - } - private static void close(SQLiteDatabase db) { - //if (db != null && db.isOpen()) db.close(); - } + //删除中介表 + mDelegate.dropTable(db, tableName + "_temp"); - private static SQLiteDatabase checkDb(SQLiteDatabase db) { - if (db == null || !db.isOpen()) { - db = INSTANCE.getWritableDatabase(); + mDelegate.close(db); } - return db; } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java index 877f10dd..525f90cd 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -16,6 +16,14 @@ package com.arialyy.aria.orm; import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Default; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.NoNull; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Primary; +import com.arialyy.aria.orm.annotation.Wrapper; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -32,31 +40,41 @@ import java.util.Set; final class SqlUtil { /** - * 获取一对一参数 + * 获取主键字段名 */ - static String getOneToOneParams(Field field) { - OneToOne oneToOne = field.getAnnotation(OneToOne.class); - if (oneToOne == null) { - throw new IllegalArgumentException("@OneToOne注解的对象必须要有@Primary注解的字段"); + static String getPrimaryName(Class clazz) { + List fields = CommonUtil.getAllFields(clazz); + String column; + if (fields != null && !fields.isEmpty()) { + + for (Field field : fields) { + field.setAccessible(true); + if (isPrimary(field)) { + column = field.getName(); + return column; + } + } } - return oneToOne.table().getName() + "$$" + oneToOne.key(); + return null; } /** - * 获取List一对多参数 - * - * @param field list反射字段 + * 获取类中所有不被忽略的字段 */ - static String getOneToManyElementParams(Field field) { - OneToMany oneToMany = field.getAnnotation(OneToMany.class); - if (oneToMany == null) { - throw new IllegalArgumentException("一对多元素必须被@OneToMany注解"); + static List getAllNotIgnoreField(Class clazz) { + List fields = CommonUtil.getAllFields(clazz); + List temp = new ArrayList<>(); + if (fields != null && fields.size() > 0) { + for (Field f : fields) { + f.setAccessible(true); + if (!isIgnore(f)) { + temp.add(f); + } + } + return temp; + } else { + return null; } - //关联的表名 - String tableName = oneToMany.table().getName(); - //关联的字段 - String key = oneToMany.key(); - return tableName + "$$" + key; } /** @@ -65,10 +83,6 @@ final class SqlUtil { * @param field list反射字段 */ static String list2Str(DbEntity dbEntity, Field field) throws IllegalAccessException { - NormalList normalList = field.getAnnotation(NormalList.class); - if (normalList == null) { - throw new IllegalArgumentException("List中元素必须被@NormalList注解"); - } List list = (List) field.get(dbEntity); if (list == null || list.isEmpty()) return ""; StringBuilder sb = new StringBuilder(); @@ -85,17 +99,17 @@ final class SqlUtil { * @return 如果str为null,则返回null */ static List str2List(String str, Field field) { - NormalList normalList = field.getAnnotation(NormalList.class); - if (normalList == null) { - throw new IllegalArgumentException("List中元素必须被@NormalList注解"); - } if (TextUtils.isEmpty(str)) return null; String[] datas = str.split("\\$\\$"); List list = new ArrayList(); - String type = normalList.clazz().getName(); - for (String data : datas) { - list.add(checkData(type, data)); + Class clazz = CommonUtil.getListParamType(field); + if (clazz != null) { + String type = clazz.getName(); + for (String data : datas) { + list.add(checkData(type, data)); + } } + return list; } @@ -152,9 +166,12 @@ final class SqlUtil { } /** + * shadow$_klass_、shadow$_monitor_、{@link Ignore}、rowID、{@link Field#isSynthetic()}、{@link + * Modifier#isFinal(int)}、{@link Modifier#isStatic(int)}将被忽略 + * * @return true 忽略该字段 */ - static boolean ignoreField(Field field) { + static boolean isIgnore(Field field) { // field.isSynthetic(), 使用as热启动App时,AS会自动给你的class添加change字段 Ignore ignore = field.getAnnotation(Ignore.class); int modifiers = field.getModifiers(); @@ -164,19 +181,29 @@ final class SqlUtil { .isStatic(modifiers) || Modifier.isFinal(modifiers); } + /** + * 判断是否是Wrapper注解 + * + * @return {@code true} 是 + */ + static boolean isWrapper(Class clazz) { + Wrapper w = (Wrapper) clazz.getAnnotation(Wrapper.class); + return w != null; + } + /** * 判断是否一对多注解 */ - static boolean isOneToMany(Field field) { - OneToMany oneToMany = field.getAnnotation(OneToMany.class); + static boolean isMany(Field field) { + Many oneToMany = field.getAnnotation(Many.class); return oneToMany != null; } /** * 判断是否是一对一注解 */ - static boolean isOneToOne(Field field) { - OneToOne oneToOne = field.getAnnotation(OneToOne.class); + static boolean isOne(Field field) { + One oneToOne = field.getAnnotation(One.class); return oneToOne != null; } @@ -210,6 +237,16 @@ final class SqlUtil { return nn != null; } + /** + * 判断是否是default + * + * @return {@code true}为default + */ + static boolean isDefault(Field field) { + Default nn = field.getAnnotation(Default.class); + return nn != null; + } + private static Object checkData(String type, String data) { if (type.equalsIgnoreCase("java.lang.String")) { return data; @@ -222,17 +259,4 @@ final class SqlUtil { } return null; } - - /** - * 查找class的主键字段 - * - * @return 返回主键字段名 - */ - static String getPrimaryName(Class clazz) { - List fields = CommonUtil.getAllFields(clazz); - for (Field field : fields) { - if (isPrimary(field)) return field.getName(); - } - return null; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Foreign.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java similarity index 79% rename from Aria/src/main/java/com/arialyy/aria/orm/Foreign.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java index cc577d24..dcc81434 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Foreign.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; + +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -21,16 +22,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Created by AriaL on 2017/7/4. - * 外键约束 + * Created by lyy on 2015/11/2. + * 默认数据 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Foreign { - /** - * 关联的表 - */ - Class table(); - /** - * 关联的列 - */ - String column(); -} +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Default { + String value() default ""; +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java new file mode 100644 index 00000000..6371a4d7 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm.annotation; + +import com.arialyy.aria.orm.ActionPolicy; +import com.arialyy.aria.orm.DbEntity; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by AriaL on 2017/7/4. + * 外键约束 + */ +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Foreign { + + /** + * 关联的表 + */ + Class parent(); + + /** + * 父表对应的列名 + */ + String column(); + + /** + * ON UPDATE 约束 + */ + ActionPolicy onUpdate() default ActionPolicy.NO_ACTION; + + /** + * ON DELETE 约束 + */ + ActionPolicy onDelete() default ActionPolicy.NO_ACTION; +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Ignore.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/orm/Ignore.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java index 33c4523f..d8a9fe0d 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Ignore.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java similarity index 83% rename from Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java index 8f529e1b..59d5367c 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -24,13 +24,14 @@ import java.lang.annotation.Target; * Created by AriaL on 2017/7/4. * 一对多 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface OneToMany { +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Many { /** - * 关联的表 + * 父表对应的字段名 */ - Class table(); + String parentColumn(); + /** - * 关联的主键 + * 父表在子表对应的字段 */ - String key(); + String entityColumn(); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/NoNull.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/orm/NoNull.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java index 212efc4b..d29c8ad9 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/NoNull.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java similarity index 82% rename from Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java index 7641fd55..3f8df08e 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -22,17 +22,8 @@ import java.lang.annotation.Target; /** * Created by AriaL on 2017/7/4. - * 一对一 + * 一 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface OneToOne { +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface One { - /** - * 关联的表 - */ - Class table(); - - /** - * 关联的主键 - */ - String key(); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Primary.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java similarity index 87% rename from Aria/src/main/java/com/arialyy/aria/orm/Primary.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java index 365f1713..4ac2fb1c 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Primary.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -27,4 +27,9 @@ import java.lang.annotation.Target; */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Primary { int value() default -1; + + /** + * 字段需要int类型才可以自增 + */ + boolean autoincrement() default false; } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java new file mode 100644 index 00000000..e2cbbb94 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.aria.orm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by lyy on 2015/11/2. + * 设置表名,如果不是使用该注解,默认为类名 + */ +@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Table { + String tableName(); +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/PrimaryAndForeign.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java similarity index 72% rename from Aria/src/main/java/com/arialyy/aria/orm/PrimaryAndForeign.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java index 2b50b18d..e0894440 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/PrimaryAndForeign.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -21,16 +21,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Created by AriaL on 2017/7/4. - * 主键和外键约束 + * Created by laoyuyu on 2018/3/21. + * 关系包裹 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface PrimaryAndForeign { - /** - * 关联的表 - */ - Class table(); - /** - * 关联的列 - */ - String column(); +@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) +public @interface Wrapper { } diff --git a/Aria/src/main/java/com/arialyy/aria/util/ALog.java b/Aria/src/main/java/com/arialyy/aria/util/ALog.java index f24a3a30..c49a1ca9 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/ALog.java +++ b/Aria/src/main/java/com/arialyy/aria/util/ALog.java @@ -25,7 +25,7 @@ import android.util.Log; * Aria日志工具 */ public class ALog { - + public static final boolean DEBUG = true; public static final int LOG_LEVEL_VERBOSE = 2; public static final int LOG_LEVEL_DEBUG = 3; public static final int LOG_LEVEL_INFO = 4; diff --git a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java index 65cd9b53..2e61f8c1 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -74,7 +74,8 @@ public class CheckUtil { * 检查下载实体 */ public static void checkDownloadEntity(DownloadEntity entity) { - entity.setUrl(checkUrl(entity.getUrl())); + checkUrlInvalidThrow(entity.getUrl()); + entity.setUrl(entity.getUrl()); checkPath(entity.getDownloadPath()); } @@ -88,9 +89,9 @@ public class CheckUtil { } /** - * 检测下载链接是否合法,如果地址中path是"//"而不是"/"将会改为"/"; + * 检测url是否合法,如果url不合法,将抛异常 */ - public static String checkUrl(String url) { + public static void checkUrlInvalidThrow(String url) { if (TextUtils.isEmpty(url)) { throw new IllegalArgumentException("url不能为null"); } else if (!url.startsWith("http") && !url.startsWith("ftp")) { @@ -100,7 +101,26 @@ public class CheckUtil { if (index == -1) { throw new IllegalArgumentException("url不合法"); } - return url; + } + + /** + * 检测url是否合法 + * + * @return {@code true} 合法,{@code false} 非法 + */ + public static boolean checkUrl(String url) { + if (TextUtils.isEmpty(url)) { + ALog.e(TAG, "url不能为null"); + return false; + } else if (!url.startsWith("http") && !url.startsWith("ftp")) { + ALog.e(TAG, "url【" + url + "】错误"); + return false; + } + int index = url.indexOf("://"); + if (index == -1) { + ALog.e(TAG, "url【" + url + "】不合法"); + } + return true; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java index f52ef139..eb9da319 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -30,13 +30,10 @@ import com.arialyy.aria.core.command.group.GroupCmdFactory; import com.arialyy.aria.core.command.normal.AbsNormalCmd; import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.AbsGroupTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTaskEntity; -import com.arialyy.aria.orm.DbEntity; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -46,11 +43,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; -import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URLEncoder; @@ -62,6 +56,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; @@ -73,6 +68,64 @@ import java.util.regex.Pattern; public class CommonUtil { private static final String TAG = "CommonUtil"; + /** + * 获取map泛型类型 + * + * @param map list类型字段 + * @return 泛型类型 + */ + public static Class[] getMapParamType(Field map) { + Class type = map.getType(); + if (!type.isAssignableFrom(Map.class)) { + ALog.d(TAG, "字段类型不是Map"); + return null; + } + + Type fc = map.getGenericType(); + + if (fc == null) { + ALog.d(TAG, "该字段没有泛型参数"); + return null; + } + + if (fc instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) fc; + Type[] types = pt.getActualTypeArguments(); + Class[] clazz = new Class[2]; + clazz[0] = (Class) types[0]; + clazz[1] = (Class) types[1]; + return clazz; + } + return null; + } + + /** + * 获取list泛型类型 + * + * @param list list类型字段 + * @return 泛型类型 + */ + public static Class getListParamType(Field list) { + Class type = list.getType(); + if (!type.isAssignableFrom(List.class)) { + ALog.d(TAG, "字段类型不是List"); + return null; + } + + Type fc = list.getGenericType(); // 关键的地方,如果是List类型,得到其Generic的类型 + + if (fc == null) { + ALog.d(TAG, "该字段没有泛型参数"); + return null; + } + + if (fc instanceof ParameterizedType) { //如果是泛型参数的类型 + ParameterizedType pt = (ParameterizedType) fc; + return (Class) pt.getActualTypeArguments()[0]; //得到泛型里的class类型对象。 + } + return null; + } + /** * 创建文件名,如果url链接有后缀名,则使用url中的后缀名 * @@ -230,55 +283,6 @@ public class CommonUtil { return null; } - /** - * 实例化泛型的实际类型参数 - * - * @throws Exception - */ - public static void typeCheck(Type type) throws Exception { - System.out.println("该类型是" + type); - // 参数化类型 - if (type instanceof ParameterizedType) { - Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); - for (int i = 0; i < typeArguments.length; i++) { - // 类型变量 - if (typeArguments[i] instanceof TypeVariable) { - System.out.println("第" + (i + 1) + "个泛型参数类型是类型变量" + typeArguments[i] + ",无法实例化。"); - } - // 通配符表达式 - else if (typeArguments[i] instanceof WildcardType) { - System.out.println("第" + (i + 1) + "个泛型参数类型是通配符表达式" + typeArguments[i] + ",无法实例化。"); - } - // 泛型的实际类型,即实际存在的类型 - else if (typeArguments[i] instanceof Class) { - System.out.println("第" + (i + 1) + "个泛型参数类型是:" + typeArguments[i] + ",可以直接实例化对象"); - } - } - // 参数化类型数组或类型变量数组 - } else if (type instanceof GenericArrayType) { - System.out.println("该泛型类型是参数化类型数组或类型变量数组,可以获取其原始类型。"); - Type componentType = ((GenericArrayType) type).getGenericComponentType(); - // 类型变量 - if (componentType instanceof TypeVariable) { - System.out.println("该类型变量数组的原始类型是类型变量" + componentType + ",无法实例化。"); - } - // 参数化类型,参数化类型数组或类型变量数组 - // 参数化类型数组或类型变量数组也可以是多维的数组,getGenericComponentType()方法仅仅是去掉最右边的[] - else { - // 递归调用方法自身 - typeCheck(componentType); - } - } else if (type instanceof TypeVariable) { - System.out.println("该类型是类型变量"); - } else if (type instanceof WildcardType) { - System.out.println("该类型是通配符表达式"); - } else if (type instanceof Class) { - System.out.println("该类型不是泛型类型"); - } else { - throw new Exception(); - } - } - /** * 根据下载任务组的url创建key * @@ -308,28 +312,27 @@ public class CommonUtil { * {@code false}如果任务已经完成,只删除任务数据库记录 */ public static void delDownloadGroupTaskConfig(boolean removeFile, - DownloadGroupTaskEntity tEntity) { - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", tEntity.key); - if (tasks != null && !tasks.isEmpty()) { - for (DownloadTaskEntity taskEntity : tasks) { - delDownloadTaskConfig(removeFile, taskEntity); - } + DownloadGroupEntity groupEntity) { + if (groupEntity == null) { + ALog.e(TAG, "删除下载任务组记录失败,任务组实体为null"); + return; } - if (tEntity.getEntity() != null) { - File dir = new File(tEntity.getEntity().getDirPath()); - if (removeFile) { - if (dir.exists()) { - dir.delete(); - } - } else { - if (!tEntity.getEntity().isComplete()) { - dir.delete(); - } + for (DownloadEntity taskEntity : groupEntity.getSubTask()) { + delDownloadTaskConfig(removeFile, taskEntity); + } + + File dir = new File(groupEntity.getDirPath()); + if (removeFile) { + if (dir.exists()) { + dir.delete(); + } + } else { + if (!groupEntity.isComplete()) { + dir.delete(); } - tEntity.deleteData(); } + groupEntity.deleteData(); } /** @@ -338,8 +341,7 @@ public class CommonUtil { * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经删除完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录 */ - public static void delUploadTaskConfig(boolean removeFile, UploadTaskEntity tEntity) { - UploadEntity uEntity = tEntity.getEntity(); + public static void delUploadTaskConfig(boolean removeFile, UploadEntity uEntity) { if (uEntity == null) { return; } @@ -353,8 +355,8 @@ public class CommonUtil { if (config.exists()) { config.delete(); } - //uEntity.deleteData(); - tEntity.deleteData(); + //下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 + uEntity.deleteData(); } /** @@ -363,8 +365,7 @@ public class CommonUtil { * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经下载完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录 */ - public static void delDownloadTaskConfig(boolean removeFile, DownloadTaskEntity tEntity) { - DownloadEntity dEntity = tEntity.getEntity(); + public static void delDownloadTaskConfig(boolean removeFile, DownloadEntity dEntity) { if (dEntity == null) return; File file = new File(dEntity.getDownloadPath()); if (removeFile) { @@ -383,9 +384,8 @@ public class CommonUtil { if (config.exists()) { config.delete(); } - //dEntity.deleteData(); - //tEntity.deleteData(); - tEntity.deleteData(); + //下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 + dEntity.deleteData(); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java b/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java index 4a56c186..20258930 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java +++ b/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java @@ -35,6 +35,8 @@ public class ErrorHelp { * @param msg 错误提示 * @param ex 异常 */ + // TODO: 2018/4/4 错误应该写在文件中 + @Deprecated public static void saveError(String taskType, AbsEntity entity, String msg, String ex) { ErrorEntity errorEntity = new ErrorEntity(); errorEntity.insertTime = System.currentTimeMillis(); diff --git a/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java index 4762fe1f..7140a086 100644 --- a/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java @@ -45,7 +45,7 @@ public class DownloadGroupActivity extends BaseActivity temps = Aria.download(this).getTotleTaskList(); + List temps = Aria.download(this).getTotalTaskList(); if (temps != null && !temps.isEmpty()) { mData.addAll(temps); } diff --git a/aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java b/aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java index 83ad03af..709558e4 100644 --- a/aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java +++ b/aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java @@ -16,7 +16,9 @@ package com.arialyy.aria.core.upload; import com.arialyy.aria.core.inf.AbsNormalTaskEntity; -import com.arialyy.aria.orm.OneToOne; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.Primary; import java.util.HashMap; import java.util.Map; @@ -28,9 +30,16 @@ public class UploadTaskEntity extends AbsNormalTaskEntity { public String attachment; //文件上传需要的key public String contentType = "multipart/form-data"; //上传的文件类型 public String userAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)"; - @OneToOne(table = UploadEntity.class, key = "filePath") public UploadEntity entity; + + //@OneToOne(table = UploadEntity.class, key = "filePath") public UploadEntity entity; + + @Ignore public UploadEntity entity; + public String filePath = ""; + @Primary @Foreign(parent = UploadEntity.class, column = "filePath") + public String key; + /** * 文件上传表单 */ @@ -43,11 +52,17 @@ public class UploadTaskEntity extends AbsNormalTaskEntity { return entity; } + @Override public String getKey() { + return key; + } + public void save(UploadEntity entity) { - filePath = entity.getFilePath(); - key = entity.getKey(); - this.entity = entity; - entity.save(); + if (entity != null) { + filePath = entity.getFilePath(); + key = entity.getKey(); + this.entity = entity; + entity.save(); + } save(); } }