Day4
今日总结:
因为这部分的内容都是记在纸上,一共四页。所以博客里的内容比较少
2.重写ORM框架:
之前在使用aiomysql+sqlalchemy的学习中,因为不太熟悉ORM的操作,结果将数据库连接和ORM写在了一起,这样造成了较强的耦合,不利于SQL的扩展;
而今天的学习呢,思路更加明确了。使用aiomysql来执行SQL语句、连接、返回数据结果;SQLalchemy呢,它主要用来进行数据库关系映射ORM,以及生成SQL语句。
这样一来,思路清晰了很多;虽然aiomysql能代替一些SQLalchemy的功能,或者SQLalchemy能直接执行数据库的执行操作(使用sessionmaker),但是这样混乱的使用,
显然对程序的开发是不友好的,方便,但是降低了开发的效率;
今日学习:
-重写ORM框架,我会从这三个方面叙述:1.实现了哪些功能 2.为什么要这样实现 3.我遇到了哪些问题
1.将数据库连接单独写成一层,Class DBcontroller类,初始化连接时,会使用单例模式(创建一个pool),用户不使用相同的conn,但却在同一个pool下,解决了用户使用单一连线造成的问题(服务器压力,数据传输之类的);同时用户也不用需要手动去连接mysql服务器。
1 class DBcontroller: 2 __engine=None 3 __isinstance = False 4 def __new__(cls, *args, **kwargs): 5 if cls.__isinstance: # 如果被实例化了 6 return cls.__isinstance # 返回实例化对象 7 print('connecting to database...') 8 asyncio.get_event_loop().run_until_complete(DBcontroller.connect()) 9 cls.__isinstance = object.__new__(cls) # 否则实例化 10 return cls.__isinstance # 返回实例化的对象 11 12 @staticmethod 13 async def connect(): 14 try: 15 __engine = await create_engine(user='root', 16 db='youku', 17 host='127.0.0.1', 18 password='root', 19 minsize=1, 20 maxsize=10, 21 autocommit=True) 22 if __engine: 23 DBcontroller.__engine = __engine 24 DBcontroller.connectStatue =True 25 print('connect to mysql success!') 26 else: 27 raise ("connect to mysql error ") 28 except: 29 print('connect error.', exc_info=True) 30 31 def selectTable(self,sql): 32 res='' 33 async def select(res): 34 conn = await DBcontroller.__engine.acquire() 35 try: 36 result = await conn.execute(sql) 37 res = await result.fetchall() 38 # for row in res: 39 # print(row) 40 except Exception as e : 41 print(e) 42 finally: 43 DBcontroller.__engine.release(conn) 44 return res 45 res = asyncio.get_event_loop().run_until_complete(select(res)) 46 return res 47 48 def executeTable(self,sql): 49 res='' 50 async def execute(res,sql): 51 conn = await DBcontroller.__engine.acquire() 52 try: 53 result = await conn.execute(sql) 54 res = result.lastrowid 55 print('操作执行完毕') 56 except Exception as e: 57 if e.args[0]== 1062: 58 print('主键已存在') 59 else: 60 print(f'插入失败:{e}') 61 finally: 62 DBcontroller.__engine.release(conn) 63 return res 64 lastrowid = asyncio.get_event_loop().run_until_complete(execute(res,sql)) 65 return lastrowid
2.自动生成Model.py,但前提必须使用sqlalchemy,在CMD里使用这串代码,就可以自动生成Model.py,解放了时间:
sqlacodegen mysql+pymysql://root:root@127.0.0.1:3306/automovie > models.py
自动生成的Model里的类,也带有__table__,__mapper___等和sqlalchemy相同属性的类
3.使用sqlalchemy生成sql语句,例如User类,ygy=User(name='杨归元',age=18),
使用sqlchemy.select('*').select_from(User.__table__).where(User.__table__.c.age==ygy.age),
那么可以生成"SELECT * FROM USER WHERE USER.AGE = 18"的句子;这里也就是为什么要使用ORM的原因吧!
1.为了防止SQL注入,用户不可能通过输入属性值来造成sql注入,因为中间有一层ORM;SQL没有直接裸露出来
2.为了是开发者,能够更加专注的去写业务逻辑层,不用去担心SQL语句到底要去怎么写;(当然直接写SQL也是种另外的感受)
4.类似的语句还有这样的:self.__table__.update().values(attr).where(where)
具体网址在这里:https://www.osgeo.cn/sqlalchemy/core/dml.html
5.为了获取user的属性,我去找了父类sqlalchemy.ext.declarative.declarative_base,去这里面的函数一个一个去看,然后又去百度了各种官方文档,
最后无解,时间都浪费在了这里,最后的解决方法是这样的,
使用user.__dict__,获取属性;
如果遇到inser操作时,插入成功了,那么我们要把user剩下的属性给返回,比如user.datatime,user.id;这些都是需要更新的;
所以这个时候select一次user,然后将获得的属性值赋值给user.__dict__,结局失败了,原因不知;
最后的解决方法是这样的:select返回一个对象,从数据库里取出属性,然后根据这个属性列表新建一个相应的对象
6.如何使select方法返回一个对象?
conn.execute(sql)之后,会返回一个resultProxy对象,
可以for key, value in result.items()来获取列名和列表值:
key:id value:1
...
key:age value:18
同时通过,type(self)获取它的类名
那么 newuser = type(self)(),就可以新建了一个相同的对象
7.当执行 result = await conn.execute(insertSQL)后,result.fetchall()会报异常,但是对系统运行没有影响:
这里有关mysql的一个叫Nocount的东西:
Set Nocount on
当SET NOCOUNT on时候,将不向客户端发送存储过程每个语句的DONE_IN_proc消息,如果存储过程中包含一些并不返回实际数据的语句,网络通信流量便会大量减少,可以显著提高应用程序性能;
插入执行结束后,没有返回值,但是我们却获取了返回值;但我们可以获取result.lastrowid,把刚插入的数据的id拿到
8.以后再也不想研究ORM了。。。
但是通过这几天的学习,我感觉自己的收获很多,从数据库引擎到数据库,从数据库到ORM,各方各面,条条划划的地方都有学习到,也算是不错的收获