zoukankan      html  css  js  c++  java
  • python学习-python与mysql交互

    一、使用pymysql进行交互

    安装::: pip install pymysql

    pymysql主要是使用原生sql与mysql进行交互,示例如下:

    import pymysql
    #创建连接
    conn = pymysql.connect(host='192.168.0.26',port=3306,user='xll',
                           passwd='xll123',db='mysql'
                           )
    #创建游标
    cursor = conn.cursor()
    #执行sql,并返回受影响的行数
    effect_row = cursor.execute("select user,host from user;")
    print(effect_row)
    
    #
    effect_row1 = cursor.executemany("insert into hosts (id,name) values (%s,%s)",[(1,'insert'),(2,'error')])
    
    #提交,不然无法完成数据的提交
    conn.commit()
    
    #关闭游标
    cursor.close()
    #关闭连接
    conn.close()
    

    2、获取自增ID

    import pymysql
    
    conn = pymysql.connect(host='192.168.0.26',port=3306,user='xll',
                           passwd='xll123',db='testdb')
    
    cursor = conn.cursor()
    cursor.executemany("insert into hosts(id,name) values(%s,%s)",
                       [(3,'host3'),(4,'host4')]
                       )
    conn.commit()
    cursor.close()
    conn.close()
    #获取最新自增ID
    new_id = cursor.lastrowid
    print(new_id)

    3、执行查询,获取数据

    import pymysql
    
    conn = pymysql.connect(host='192.168.0.26', port=3306, user='xll', passwd='xll123', db='testdb')
    cursor = conn.cursor()
    cursor.execute("select * from hosts")
    
    # 获取第一行数据
    #row_1 = cursor.fetchone()
    #print(row_1)
    
    # 获取前n行数据
    #row_2 = cursor.fetchmany(3)
    # 获取所有数据
    row_3 = cursor.fetchall()
    #print(row_2)
    print(row_3)
    conn.commit()
    cursor.close()
    conn.close()
    

    注:

    1. 在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:

    • cursor.scroll(1,mode='relative')  # 相对当前位置移动
    • cursor.scroll(2,mode='absolute') # 相对绝对位置移动

    2. fetch数据时,已经fetch过得数据,再fetch的时候,只会取后面的数据,已经fetch过得数据,不会再fetch了。

    3、fetch获取不同的数据类型

    import pymysql
    
    conn = pymysql.connect(host='192.168.0.26', port=3306, user='xll', passwd='xll123', db='testdb')
    #cursor = conn.cursor()
    #游标设置为字典类型
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    
    cursor.execute("select * from hosts")
    
    # 获取所有数据
    row_3 = cursor.fetchall()
    #print(row_2)
    print(row_3)
    conn.commit()
    cursor.close()
    conn.close()
    

      

    二、SQLAlchemy orm 方式

      orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。

     

    orm的优点:

    1. 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
    2. ORM使我们构造固化数据结构变得简单易行。

    缺点:

    1. 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。

    安装:::

    在Python中,最有名的ORM框架是SQLAlchemy。用户包括openstack\Dropbox等知名公司或应用,主要用户列表http://www.sqlalchemy.org/organizations.html#openstack

    Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    MySQL-Python
        mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
        
    pymysql
        mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
        
    MySQL-Connector
        mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
        
    cx_Oracle
        oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
        
    更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html

     安装sqlalchemy:

    1
    2
    pip install SQLAlchemy
    pip install pymysql   #由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互

     sqlalchemy基本使用

    2.1、原生sql创建表

    1
    2
    3
    4
    5
    6
    CREATE TABLE user (
        id INTEGER NOT NULL AUTO_INCREMENT,
        name VARCHAR(32),
        password VARCHAR(64),
        PRIMARY KEY (id)
    )

    2.2、利用orm创建一张表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column,Integer,String
    from sqlalchemy.orm import  sessionmaker
     
     
    engine = create_engine("mysql+pymysql://xll:xll123@192.168.0.26:3306/testdb",
                           encoding="utf-8",echo=True)    #连接数据库,echo=True =>把所有的信息都打印出来
     
    Base = declarative_base() #生成orm基类
     
    class User(Base):
        __tablename__ = "user" #表名
        id = Column(Integer,primary_key=True)
        name = Column(String(32))
        passwd = Column(String(64))
     
    Base.metadata.create_all(engine)  #这边的意思是创建定义所有的表

    2.3、往表里面插入数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from sqlalchemy.orm import  sessionmaker
     
    Session_class = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
    Session = Session_class() #生成session实例 ,相当于创建游标cursor
      
      
    user_obj = User(name="xll",passwd="xll123"#生成你要创建的数据对象
    print(user_obj.name,user_obj.id)  #此时还没创建对象呢,不信你打印一下id发现还是None
      
    Session.add(user_obj) #把要创建的数据对象添加到这个session里, 一会统一创建
    print(user_obj.name,user_obj.id#此时也依然还没创建
      
    Session.commit() #现此才统一提交,创建数据

    2.4 向表里插入数据

    插入一条数据:

    obj = Users(name="test", passwd='1234')
    session.add(obj)  #新增一条数据
    session.commit()
    

    插入多条数据:

    obj1 = Users(name="test",passwd='12345')
    obj2 = Users(name="test12", extra='123qwe')
    session.add_all([obj1,obj2,])  #插入多条记录
    session.commit()
    

    2.5 删除数据

    session.query(Users).filter(Users.id > 2).delete()  #删除符合条件的
    session.commit()
    

    2.6 查询数据

    2.6.1 查询所有字段

       原生sql如下:

    SELECT * FROM USER WHERE NAME="xll"
    

    ①查询符合条件的所有数据

    1
    2
    3
    4
    5
    data = Session.query(User).filter_by(name='xll').all()  #查询符合条件的所有数据,如果不需要条件,filter_by()就行
    print(data)
     
    输出:
    [<__main__.User object at 0x000001D4C92EC668>   #把返回的数据映射成内存对象

    获取数据:

    1
    2
    3
    4
    print(data[0].name,data[0].id,data[0].passwd)
     
    #输出
    xll 1 xll123

    ②查询符合条件的第一条数据

    1
    2
    3
    4
    5
    data = Session.query(User).filter_by(name='xll').first()  #查询满足条件的第1条数据
    print(data)
     
    #输出
    <__main__.User object at 0x000001FBEBD499B0>  #同样返回的是内存对象

    获取数据:

    1
    2
    3
    4
    print(data.name,data.id,data.passwd)
     
    #输出
    xll 1 xll123

    不过刚才上面的显示的内存对象对址你是没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class User(Base):
        __tablename__ = "user" #表名
        id = Column(Integer,primary_key=True)
        name = Column(String(32))
        passwd = Column(String(64))
     
        def __repr__(self):    #使返回的内存对象变的可读
            return  "<id:{0} name:{1}>".format(self.id,self.name)
     
    Base.metadata.create_all(engine)  #这边的意思是创建定义所有的表
     
    Session_class = sessionmaker(bind=engine)
    Session = Session_class()   #创建session会话,相当于创建一个cursor
     
     
    data = Session.query(User).filter_by().all()  #获取所有数据
    print(data)
     
    #输出
    [<id:1 name:xll>, <id:2 name:test>, <id:3 name:test12>]

    2.6.2 查询指定字段

    原生SQL:

    1
    select user.name,user.passwd from user where name="xll"

     orm表示:

    1
    2
    3
    4
    5
    data = Session.query(User.name,User.passwd).filter_by(name='xll').first()  #只查询name和passwd的信息
    print(data.name,data.passwd)
     
    #输出
    xll xll123

    2.6.3 所有查询

    ret = session.query(Users).all()  #查询所有
    ret = session.query(Users.name, Users.extra).all()  #只查询name和extra字段
    ret = session.query(Users).filter_by(name='xll').all() #查询name='xll'的所有数据
    ret = session.query(Users).filter_by(name='xll').first()#查询name='xll'的第一条数据
    #查询以User.id排序的数据
    ret = session.query(Users).filter(text("id<:value and name=:name")).params(value=4, name='test12').order_by(User.id).all() 
    #根据原生sql查询数据
    ret = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='test').all()
    

    2.6.4 查询比较大小的

    说明:之前我们过滤都是filter_by,现在我们用filter过滤,都是过滤比较数值大小的,比如:>,<,==等。

    1
    2
    3
    4
    5
    data = Session.query(User).filter(User.id>2).all()
    print(data)
     
    #输出
    [<id:2 name:test12>]

    2.6.5 多条件查询

    data = Session.query(User).filter(User.id>2).filter(User.id<=4).all()
    print(data)
    

      相当于select * from user where id>2 and id<=4

    2.6.6 count函数

    data = Session.query(User).filter(User.name.like("%xll%")).count()  #模糊匹配xll的个数
    print(data)
    #相当于原生sql  select count(*) from user where name like '%xll%'
    

    2.6.7 分组统计,需要调用函数 func

    from sqlalchemy import func
     
    data = Session.query(func.count(User.name),User.name).group_by(User.name).all()  #根据User.name分组
    print(data)
    
    #相当于原生sql select count(user.name),user.name from user user group by user.name
    

    2.6.8 条件查询

    ret = session.query(User).filter_by(name='xll').all()
    ret = session.query(User).filter(User.id > 1, User.name == 'xll').all()
    ret = session.query(User).filter(User.id.between(1, 3), User.name == 'xll').all()
    ret = session.query(User).filter(User.id.in_([1,3,4])).all()
    ret = session.query(User).filter(~User.id.in_([1,3,4])).all()
    ret = session.query(User).filter(User.id.in_(session.query(User.id).filter_by(name='xll'))).all()
    

    2.6.9 and 和or 关联

    from sqlalchemy import and_, or_
    ret = session.query(User).filter(and_(User.id > 3, User.name == 'xll')).all()
    ret = session.query(User).filter(or_(User.id < 2, User.name == 'xll')).all()
    ret = session.query(User).filter(
        or_(
            User.id < 2,
            and_(User.name == 'xll', Users.id > 3),
            User.extra != ""
        )).all()
    

    2.6.10 排序

    ret = session.query(User).order_by(User.name.desc()).all()
    ret = session.query(User).order_by(User.name.desc(), User.id.asc()).all()
    

    2.6.11 其他

    #通配符 %
    ret = session.query(Users).filter(Users.name.like('xll%')).all()
    ret = session.query(Users).filter(~Users.name.like('xll%')).all()
    
    #限制返回数据的条数
    ret = session.query(User)[1:2]
    
    #分组 含最大、最小和和值统计
    from sqlalchemy.sql import func
     
    ret = session.query(User).group_by(User.name).all()
    ret = session.query(
        func.max(User.id),
        func.sum(User.id),
        func.min(User.id)).group_by(User.name).all()
     
    #分组中的条件查询 having 关键字
    ret = session.query(
        func.max(User.id),
        func.sum(User.id),
        func.min(User.id)).group_by(User.name).having(func.min(User.id) >2).all()
    

      

    2.7 更新数据 对应原生sql里的 update

    第一种方式

    1
    2
    3
    data = Session.query(User).filter_by(name="xll").first()  #获取数据
    data.name = "xll12"  #修改数据
    Session.commit()   #提交

    ②第二种方式

    1
    2
    3
    4
    session.query(Users).filter(Users.id 2).update({"name" "099"})
    session.query(Users).filter(Users.id 2).update({Users.name: Users.name + "099"}, synchronize_session=False)
    session.query(Users).filter(Users.id 2).update({"num": Users.num + 1}, synchronize_session="evaluate")
    session.commit()

    更新时回滚:

    new_user = User(name="rain",passwd="0305")
    Session.add(new_user)
     
    print(Session.query(User).filter(User.name.in_(['xll','rain'])).all() )  #这时看session里有你刚添加和修改的数据
     
    Session.rollback()
     
    print(Session.query(User).filter(User.name.in_(['xll','rain'])).all() ) #再查就发现刚才添加的数据没有了。
    

    三、外键关联

    表关系图

    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column,Integer,String,DATE,ForeignKey  #导入外键
    from sqlalchemy.orm import  relationship  #创建关系
     
    engine = create_engine("mysql+pymysql://xll:xll123@192.168.0.26:3306/testdb",
                           encoding="utf-8")
     
    Base = declarative_base() #生成orm基类
     
    class Student(Base):
        __tablename__ = "student"
        id = Column(Integer,primary_key=True)
        name = Column(String(32),nullable=False)
        register_day = Column(DATE,nullable=False)
     
        def __repr__(self):
            return "<{0} name:{1}>".format(self.id,self.name)
     
     
    class StudyRecord(Base):
        __tablename__ = "study_record"
        id = Column(Integer,primary_key=True)
        day = Column(Integer,nullable=False)
        status = Column(String(32),nullable=False)
        stu_id = Column(Integer,ForeignKey("student.id"))   #关联外键
       #relationship表示,允许你在student表里通过backref字段反向查出所有它在study_record表里的关联项
        student = relationship("Student",backref="my_study_record") 
     
        def __repr__(self):
            return "<{0} day:{1} stu_id:{2}>".format(self.student.name,self.day,self.stu_id)
     
    Base.metadata.create_all(engine) #创建表
    

     relationship 的作用:关联student表,然后我只需要在study_record里通过student这个字段,就可以去查Student类里面所有的字段,反过来利用backref="my_study_record"中的my_study_record,在student表里通过my_study_record这个字段反查study_record类里面的所有字段,然后代表着我的student在这里只需写上stu_obj.my_study_record就可以获取study_record的数据

    from sqlalchemy.orm import sessionmaker
    
    Session_class = sessionmaker(bind=engine)
    session = Session_class()
    '''以下注释为向数据库表中插入数据'''
    s1 = Student(name='alex',register_day='2014-01-01')
    s2 = Student(name='jack',register_day='2014-05-01')
    s3 = Student(name='lampard',register_day='2014-10-01')
    s4 = Student(name='terry',register_day='2014-12-01')
    s5 = Student(name='david',register_day='2015-02-01')
    study_obj1 =StudyRecord(day=1,status='YES',stu_id=1)
    study_obj2 =StudyRecord(day=2,status='NO',stu_id=1)
    study_obj3 =StudyRecord(day=3,status='YES',stu_id=1)
    study_obj4 =StudyRecord(day=1,status='YES',stu_id=2)
    study_obj5 =StudyRecord(day=1,status='YES',stu_id=3)
    study_obj6 =StudyRecord(day=2,status='YES',stu_id=2)
    study_obj7 =StudyRecord(day=2,status='YES',stu_id=3)
    session.add_all([s1,s2,s3,s4,s5,study_obj1,study_obj2,study_obj3,study_obj4,study_obj5,study_obj6,study_obj7])
    session.commit()
    '''进行外键查询'''
    stu_obj = session.query(Student).filter(Student.name=='alex').first()
    #stu_obj 的结果是Student里的一个值。下边通过my_study_record来反查study_record print(stu_obj.my_study_record)

    结果如下:

    [<alex day:1 stu_id:1>, <alex day:2 stu_id:1>, <alex day:3 stu_id:1>]
    

    注意: relationship只是在内存里面存了这么一个关联,并没有在数据库里面存这个关系,使它变的更加简单。

           这个查询通过stu_obj来关联my_study_record 来反差study_record 表里的相关内容。

    四、多外键关联

    import sqlalchemy
    from sqlalchemy import create_engine
    from sqlalchemy import Column,Integer,String,DATE,ForeignKey
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker,relationship
    
    engine = create_engine("mysql+pymysql://xll:xll123@192.168.0.26/testdb",encoding='utf-8')
    Base = declarative_base() #生成orm基类
    
    class Customer(Base):
        __tablename__ = "customer"
        id = Column(Integer,primary_key=True)
        name = Column(String(32))
        billing_address_id = Column(Integer,ForeignKey("address.id"))
        posting_address_id = Column(Integer,ForeignKey("address.id"))
    
        billing_address = relationship("Address",foreign_keys=[billing_address_id])
        posting_address = relationship("Address",foreign_keys=[posting_address_id])
    
    class Address(Base):
        __tablename__ = 'address'
        id = Column(Integer,primary_key=True)
        street = Column(String(64))
        city = Column(String(64))
        state = Column(String(64))
      def __repr__(self):
      return "<state:%s,city:%s,street:%s>" %(self.state,self.city,self.street)

    Base.metadata.create_all(engine) #创建表结构 Session_class = sessionmaker(bind=engine) #创建与数据库的会话 Session = Session_class() #生成Session实例,相当于cursor # '''以下注释为向数据库表中插入数据''' # A1 = Address(street='xizhan',city='qilihe',state='lanzhou') # A2 = Address(street='wudaokou',city='haidian',state='beijing') # A3 = Address(street='tongweilu',city='xuanwu',state='nanjing') # Session.add_all([A1,A2,A3]) # C1 = Customer(name='xll',billing_address=A1,posting_address=A1) # C2 = Customer(name='xyz',billing_address=A2,posting_address=A3) # C3 = Customer(name='liux',billing_address=A1,posting_address=A2) # C4 = Customer(name='zhangd',billing_address=A3,posting_address=A1) # # Session.add_all([C1,C2,C3,C4]) # Session.commit()

     ###插入customer表时已体现了外键关联的关系,多外键关联主要是需要在定义时区分好两个外键所对应的键

        billing_address_id = Column(Integer,ForeignKey("address.id"))
        posting_address_id = Column(Integer,ForeignKey("address.id"))
        billing_address = relationship("Address",foreign_keys=[billing_address_id])
        posting_address = relationship("Address",foreign_keys=[posting_address_id])
    

    # 多外键关联时执行查询

    obj = Session.query(Customer).filter_by(name="liux").first()
    print(obj.name,obj.billing_address,obj.posting_address)
    
    ##执行结果
    #liux <state:lanzhou,city:qilihe,street:xizhan> <state:beijing,city:haidian,street:wudaokou>
    

    五、多对多关联

    现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是:

    1. 一本书可以有好几个作者一起出版
    2. 一个作者可以写好几本书

    此时你会发现,用之前学的外键好像没办法实现上面的需求了,因为:

    当然你更不可以像下面这样干,因为这样就你就相当于有多条书的记录了,太low b了,改书名还得都改。。。

    这两种情况,都发现数据时冗余的,出现了很多重复的信息,这样可不行

    5.1、表结构

    如果遇到这种情况的话,我们可以搞出一张中间表出来

    5.2、表数据

    说明:这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联

     5.3 orm创建代码

    from sqlalchemy import Table,Column,Integer,String,DATE,ForeignKey
    from sqlalchemy.orm import relationship
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import create_engine
     
     
    Base = declarative_base() #创建orm基类
     
    book_m2m_author = Table("book_m2m_author",Base.metadata,
                            Column("id",Integer,primary_key=True),
                            Column('books_id',Integer,ForeignKey("books.id")),
                            Column('authors_id',Integer,ForeignKey("authors.id")))
     
    class Book(Base):
        __tablename__ = "books"
        id = Column(Integer,primary_key=True)
        name = Column(String(64))
        pub_date = Column(DATE)
        authors = relationship("Author",secondary=book_m2m_author,backref="books")
     
        def __repr__(self):
            return self.name
     
    class Author(Base):
        __tablename__ = "authors"
        id = Column(Integer,primary_key=True)
        name = Column(String(32))
     
        def __repr__(self):
            return self.name
     
    engine = create_engine("mysql+pymysql://xll:xll123@192.168.0.26:3306/testdb",
                           encoding="utf-8")
     
    Base.metadata.create_all(engine)

    注意了,这里面有这种创建表结构方式,跟我们之前的不太一样:

    1
    2
    3
    4
    book_m2m_author = Table("book_m2m_author",Base.metadata,
                            Column("id",Integer,primary_key=True),
                            Column('books_id',Integer,ForeignKey("books.id")),
                            Column('authors_id',Integer,ForeignKey("authors.id")))

    这张表对用户来讲的是不需要关心的,是orm自动帮你维护的,在自动帮你维护的情况下,不需要操作它了,不需要搞映射关系了,通过这个命令Table 去创建一个表,这张表通过外键已经帮我们关联了,但是通过orm的查询的时候,我们还是需要建立下面的关系的:

    1
    authors = relationship("Author",secondary=book_m2m_author,backref="books")

     这一句话表名,在orm查询的时候,还需要orm内存对象的一个级别的映射,如果没有具体制定,没有人知道第三张表book_m2m_author的存在,因为第3张表主动关联其他两张表。

     5.4 插入数据

    import ora_more2more
    from sqlalchemy.orm import sessionmaker
     
    session_class = sessionmaker(bind=ora_more2more.engine)
    session = session_class()
     
    b1 = m2m_table.Book(name="python",pub_date="2017-08-08")
    b2 = m2m_table.Book(name="sb",pub_date="2017-10-08")
    b3 = m2m_table.Book(name="zb",pub_date="2017-11-08")
     
    a1 = m2m_table.Author(name="sbhong")
    a2 = m2m_table.Author(name="sgaogao")
    a3 = m2m_table.Author(name="dbhong")
     
    b1.authors = [a1,a3]
    b2.authors = [a1,a2,a3]
     
    session.add_all([b1,b2,b3,a1,a2,a3])
     
    session.commit()
    

    5.5 查询数据

    import ora_more2more
    from sqlalchemy.orm import sessionmaker
    
    session_class = sessionmaker(bind=ora_more2more.engine)
    session = session_class()
    
    authors_obj = session.query(ora_more2more.Author).filter_by(name="sbhong").first()
    print(authors_obj.books)  # 通过books反查出books表中的数据
    book_obj = session.query(ora_more2more.Book).filter(ora_more2more.Book.id == 2).first()
    print(book_obj.authors)  # 通过authors反查出authors表中的数据
    
    session.commit()
    
    # 输出
    '''
    [python, sb]
    [dbhong, sgaogao, sbhong]
    
    '''
    

      

  • 相关阅读:
    2019.04.19 坦克大战
    2019.04.18 异常和模块
    2019.04.17 面向对象编程篇207
    fork操作时的copy-on-write策略
    Redis阻塞原因
    Redis持久化-fork操作
    Redis持久化-AOF重写
    Redis持久化-aof
    Redis持久化
    Shopify给左右两边布局的banner图加链接,链接失败
  • 原文地址:https://www.cnblogs.com/yunzaixiao/p/8512059.html
Copyright © 2011-2022 走看看