新版任天堂Swith命名曝光? 謠傳「Super Switch」搭配4K解析度OLED螢幕_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

那些衝著《魔物獵人:崛起》跑去訂購同捆機的獵友們,真的手腳要緩緩。其實近期一直不斷謠傳任天堂為了對抗PS5與Xbox Series X,計畫要推出Switch的高階版本「Switch Pro」。而近日友國外網友爆料,這款「Switch Pro」很有可能會被命名為「Super Switch」,同時搭載4K解析度的OLED螢幕,這名字真的很有任天堂的風格。

↑原本許多玩家都希望,在《魔物獵人:崛起》推出的時候,傳聞中的「Super Switch」也可以登場。

根據外媒報導指出,巴西記者Douglas Máximo過往就多次針對任天堂的事情進行爆料,而且準確度很高,讓不少人都在追蹤他的推特,希望能獲得最新的消息。而近日他在分享《魔物獵人:崛起》同捆機的消息時,表示「這樣看起來,傳聞中的Switch Pro應該不會與MH一起推出吧?」之後他又回應網友表示「基本上,如果未來會推出硬體更加強悍的新機的話,一開始就不會推出與遊戲同捆的主機,必須要等待任天堂的下個財政年度,才會知道他們要如何計劃。」而令人興奮的是,他在這邊留下了一個「Super Switch」的字樣,似乎是在暗示傳聞中的Switch Pro會被命名為「Super Switch」。

 

當然只有名字是不夠的,其實早在6日的時候,就有國外網友在resetera論壇上瘋狂爆料,表示任天堂正在開發提高遊戲解析度的新版Switch主機,這個計畫已經進行了快兩年,根據他的印象這版Switch的解析度會拉高到4K,代號為Aula。而且在新版的Switch主機中,不管是在散熱與電池壽命上,都會有顯著的提升。而螢幕本身是採取OLED螢幕。

 

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

而其實早在去年夏天的時候,任天堂的工作人員就曾經對一些遊戲開發者表示,請他們著手準備好4K解析度的遊戲與遊戲開發技術,甚至直接建議將原有的遊戲提供4K解析度版本的DLC方便玩家更新。

 

而由於《魔物獵人》系列一直都是非常需要硬體的怪獸,所以先前曾一度傳聞「Super Switch」會與《魔物獵人:崛起》同步推出,不過看這樣子,應該就只是玩家的希望而已。(玩過《魔物獵人:世界》後,誰受的暸回歸馬賽克獵人啦!)

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

Spring Data JPA入門及深入_網頁設計公司

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

一:Spring Data JPA簡介

  Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套JPA應用框架,可使開發者用極簡的代碼即可實現對數據庫的訪問和操作。它提供了包括增刪改查等在內的常用功能,且易於擴展!學習並使用 Spring Data JPA 可以極大提高開發效率!

  Spring Data JPA 讓我們解脫了DAO層的操作,基本上所有CRUD都可以依賴於它來實現,在實際的工作工程中,推薦使用Spring Data JPA + ORM(如:hibernate)完成操作,這樣在切換不同的ORM框架時提供了極大的方便,同時也使數據庫層操作更加簡單,方便解耦

1:Spring Data JPA與JPA和hibernate三者關係

  我在接下面的文章中都對它們三者進行擴展及應用,以及三者的封裝關係及調用關係,我下面也會以一張圖說明,如果此時有對JPA還一竅不通的可以參考我之前的一篇關於JPA文章的介紹

  關係:其實看三者框架中,JPA只是一種規範,內部都是由接口和抽象類構建的;hibernate它是我們最初使用的一套由ORM思想構建的成熟框架,但是這個框架內部又實現了一套JPA的規範(實現了JPA規範定義的接口),所有也可以稱hibernate為JPA的一種實現方式我們使用JPA的API編程,意味着站在更高的角度上看待問題(面向接口編程);Spring Data JPA它是Spring家族提供的,對JPA規範有一套更高級的封裝,是在JPA規範下專門用來進行數據持久化的解決方案。

   其實規範是死的,但是實現廠商是有很多的,這裏我對hibernate的實現商介紹,如其它的實現廠商大家可以自行去理解,因為規範在這,實現類可以更好別的,面向接口編程。

二:SpringDataJPA快速入門(完成簡單CRUD)

1:環境搭建及簡單查詢

-- 刪除庫
-- drop database demo_jpa;
-- 創建庫
create database if not exists demo_jpa charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa;
-- 創建表
create table if not exists student(
    sid int primary key auto_increment,     -- 主鍵id
    sname varchar(10) not null,             -- 姓名
    sage tinyint unsigned default 22,        -- 年齡
    smoney decimal(6,1),                    -- 零花錢
    saddress varchar(20)                    -- 住址
)charset gbk collate gbk_chinese_ci;
insert into student values
(1,"螞蟻小哥",23,8888.8,"安徽大別山"),
(2,"王二麻",22,7777.8,"安徽大別山"),
(3,"李小二",23,6666.8,"安徽大別山"),
(4,"霍元甲",23,5555.8,null),
(5,"恭弘=叶 恭弘問",22,4444.8,"安徽大別山"),
(6,"李連傑",23,3333.8,"安徽大別山"),
(7,"馬克思",20,2222.8,"安徽大別山");

MySQL簡單建表語句   重要【後面都參照這個建表語句進行】

<dependencies>
        <!--單元測試坐標  4.12為最穩定-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!--Spring核心坐標  注:導入此坐標也同時依賴導入了一些其它jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring對事務管理坐標-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring整合ORM框架的必須坐標 如工廠/事務等交由Spring管理-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring單元測試坐標-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--Spring Data JPA 核心坐標-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.10.4.RELEASE</version>
        </dependency>
        <!--導入AOP切入點表達式解析包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

        <!--Hibernate核心坐標-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <!--hibernate對持久層的操作坐標-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.10.Final</version>
        </dependency>

        <!--這下面的2個el坐標是使用Spring data jpa 必須導入的,不導入則報el異常-->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.4</version>
        </dependency>

        <!--C3P0連接池坐標-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <!--MySQL驅動坐標-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>

        <!--JAXB API是java EE 的API,因此在java SE 9.0 中不再包含這個 Jar 包。
            java 9 中引入了模塊的概念,默認情況下,Java SE中將不再包含java EE 的Jar包
            而在 java 6/7 / 8 時關於這個API 都是捆綁在一起的
            拋出:java.lang.ClassNotFoundException: javax.xml.bind.JAXBException異常加下面4個坐標
            -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>

pom.xml坐標導入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!--配置註解掃描-->
    <context:component-scan base-package="cn.xw"></context:component-scan>

    <!--配置C3P0連接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/demo_jpa"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123"></property>
    </bean>

    <!--創建EntityManagerFactory交給Spring管理,讓Spring生成EntityManager實體管理器操作JDBC-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--配置一個連接池,後期獲取連接的Connection連接對象-->
        <property name="dataSource" ref="dataSource"></property>
        <!--配置要掃描的包,因為ORM操作是基於實體類的-->
        <property name="packagesToScan" value="cn.xw.domain"></property>
        <!--配置JPA的實現廠家 實現了JPA的一系列規範-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
        </property>
        <!--JPA供應商適配器 因為我上面使用的是hibernate,所有適配器也選擇hibernate-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--指定當前操作的數據庫類型 必須大寫,底層是一個枚舉類-->
                <property name="database" value="MYSQL"></property>
                <!--是否自動創建數據庫表-->
                <property name="generateDdl" value="false"></property>
                <!--是否在運行的時候 在控制台打印操作的SQL語句-->
                <property name="showSql" value="true"></property>
                <!--指定數據庫方言:支持的語法,如Oracle和Mysql語法略有不同 org.hibernate.dialect下面的類就是支持的語法-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
                <!--設置是否準備事務休眠會話的底層JDBC連接,即是否將特定於事務的隔離級別和/或事務的只讀標誌應用於底層JDBC連接。-->
                <property name="prepareConnection" value="false"></property>
            </bean>
        </property>
        <!--JPA方言:高級特性 我下面配置了hibernate對JPA的高級特性 如一級/二級緩存等功能-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>
        <!--注入JPA的配置信息
            加載JPA的基本配置信息和JPA的實現方式(hibernate)的配置信息
            hibernate.hbm2ddl.auto:自動創建數據庫表
                create:每次讀取配置文件都會創建數據庫表
                update:有則不做操作,沒有則創建數據庫表 -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!--配置事務管理器  不同的事務管理器有不同的類 如我們當初使用這個事務管理器DataSourceTransactionManager-->
    <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <!--把配置好的EntityManagerFactory對象交由Spring內部的事務管理器-->
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
        <!--因為entityManagerFactory內部設置數據庫連接了  所有後面不用設置-->
        <!--<property name="dataSource" ref="dataSource"></property>-->
    </bean>

    <!--配置tx事務-->
    <tx:advice id="txAdvice" transaction-manager="jpaTransactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="insert*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="update*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="delete*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
            <!--如果命名規範直接使用下面2行控制事務-->
            <!--<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>-->
            <!--<tx:method name="*" propagation="REQUIRED" read-only="false"/>-->
        </tx:attributes>
    </tx:advice>

    <!--配置AOP切面-->
    <aop:config>
        <!--在日常業務中配置事務處理的都是Service層,因為這裡是案例講解,所有我直接在測試類進行
            所有我把事務配置這,也方便後期拷貝配置到真項目中-->
        <aop:pointcut id="pt1" expression="execution(* cn.xw.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

    <!--整合SpringDataJPA-->
    <!--base-package:指定持久層接口
        entity-manager-factory-ref:引用其實體管理器工廠
        transaction-manager-ref:引用事務    -->
    <jpa:repositories base-package="cn.xw.dao" entity-manager-factory-ref="entityManagerFactory"
                      transaction-manager-ref="jpaTransactionManager"></jpa:repositories>
</beans>

applicationContext.xml 配置文件(重要)

@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    //下面get/set/構造/toString都省略
    //注意:我上面的都使用包裝類,切記要使用包裝類,
    // 原因可能數據庫某個字段查詢出來的值為空 null    
}

Student實體類及映射關係

//@Repository("studentDao") 這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {
    /*
    JpaRepository<T,ID>:T:當前表的類型  ID:當前表主鍵字段類型 
        功能:用來完成基本的CRUD操作 ,因為內部定義了很多增刪改查操作
    JpaSpecificationExecutor<T>:T:當前表的類型
        功能:用來完成複雜的查詢等一些操作
    */
}

StudentDao接口

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    @Test
    public void test() {
        //查詢id為4學生
        Student student = sd.findOne(4);
        System.out.println("開始打印:" + student);
        //開始打印:Student{id=4, name='霍元甲', age=23, money=5555.8, address='null'}
    }
}

測試類

2:SpringDataJPA簡單單表接口方法查詢圖 

3:針對上面圖的一些方法示例

  在針對上面方法進行操作的時候,我們的Dao接口必須繼承JpaRepository<T,ID>和JpaSpecificationExecutor<T>(後面複雜查詢使用),大家也可以去研究一下CrudRepository類,這個類在這裏就不說了,JpaRespository類間接實現了它

①:Repository:最頂層的接口,是一個空的接口,目的是為了統一所有Repository的類型,且能讓組件掃描的時候自動識別。
②:CrudRepository :是Repository的子接口,提供CRUD的功能
③:PagingAndSortingRepository:是CrudRepository的子接口,添加分頁和排序的功能
④:JpaRepository:是PagingAndSortingRepository的子接口,增加了一些實用的功能,比如:批量操作等。
⑤:JpaSpecificationExecutor:用來做負責查詢的接口
⑥:Specification:是Spring Data JPA提供的一個查詢規範,要做複雜的查詢,只需圍繞這個規範來設置查詢條件
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    /****************************查詢操作*****************************/
    @Test   //查詢並排序
    public void findtestA() {
        //查詢全部 先對age排序后在進行id排序
        Sort sort = new Sort(Sort.Direction.DESC, "age", "id");
        List<Student> students = sd.findAll(sort);
        //打印省略
    }

    @Test   //查詢並分頁
    public void findtestB() {
        //查詢分頁  page是第幾頁 size 每頁個數  當前是第2頁查詢3個   相當limit 6,3
        Pageable pageable = new PageRequest(2, 3);
        Page<Student> page = sd.findAll(pageable);
        System.out.println("當前頁:"+page.getNumber());
        System.out.println("每頁显示條目數:"+page.getSize());
        System.out.println("總頁數:"+page.getTotalPages());
        System.out.println("結果集總數量:"+page.getTotalElements());
        System.out.println("是否是首頁:"+page.isFirst());
        System.out.println("是否是尾頁:"+page.isLast());
        System.out.println("是否有上一頁:"+page.hasPrevious());
        System.out.println("是否有下一頁:"+page.hasNext());
        System.out.println("結果集:"+page.getContent());
        /*
            當前頁:2
            每頁显示條目數:3
            總頁數:3
            結果集總數量:7
            是否是首頁:false
            是否是尾頁:true
            是否有上一頁:true
            是否有下一頁:false
            結果集:[Student{id=7, name='馬克思', age=20, money=2222.8, address='安徽大別山'}]
         */
        //總結:以後做分頁操作變容易了呀
    }

    @Test   //查詢指定學生
    public void findtesC() {
        //創建一個集合   List就是一個Iterable可迭代容器對象,因為實現了Iterable接口
        List<Integer> list = new ArrayList<Integer>() {{
            add(1);add(5);add(3);
        }};
        List<Student> students = sd.findAll(list);
        //打印省略
    }

    /****************************更新操作*****************************/
    @Test   //更新多個學生 更新學生全部地址為 安徽合肥
    @Transactional
    @Rollback(value = false)
    public void savetestA() {
        //創建2個集合  第一個集合查詢全部數據,然後把集合里對象地址改為新的放入新集合上,後面迭代更新
        List<Student> list = sd.findAll();
        System.out.println(list.size());
        List<Student> newlist = sd.findAll();
        for (int i = 0; i < list.size(); i++) {
            Student student = list.get(i);
            student.setAddress("安徽合肥");
            System.out.println(student);
            newlist.add(student);
        }
        List<Student> save = sd.save(newlist);
        //打印略
    }

    /****************************刪除操作*****************************/
    @Test   //刪除指定學生
    public void deletetest() {
        List<Integer> list = new ArrayList<Integer>() {{
            add(1);add(5);add(3);
        }};
        List<Student> all = sd.findAll(list);
        sd.deleteInBatch(all);
    }
    /**
     *  刪除方法介紹:
     *  void deleteAll():底層會一條一條刪除
     *  void delete(Iterable<? extends T> entities):底層會一條一條刪除
     *  void deleteAllInBatch():底層會生成一個帶or語句刪除
     *  void deleteInBatch(Iterable<T> entities):底層會生成一個帶or語句刪除
     *  如:Hibernate: delete from student where sid=? or sid=? or sid=?
     */
}

針對圖上面的一些方法操作

提取注意點:【添加/更新/查詢單個】

①:查詢單個getOne/findOne 【延遲加載/立即加載】

@Test   //查詢單個  立即加載
    public void findOneTest(){
        Student student = sd.findOne(3);
        System.out.println("-------分割線-------");
        System.out.println("打印對象:"+student);
        //控制台打印:
        /*
            Hibernate: select student0_.sid as sid1_0_0_, student0_.saddress as saddress2_0_0_,
            student0_.sage as sage3_0_0_, student0_.smoney as smoney4_0_0_, student0_.sname as
            sname5_0_0_ from student student0_ where student0_.sid=?    【SQL語句】
            -------分割線-------
            打印對象:Student{id=3, name='李小二', age=23, money=6666.8, address='安徽大別山'}
        */
        //說明:在一執行到方法時就會去數據庫查詢,然後把數據封裝到實體類對象上
        //底層調用: EntityManager裏面的find()方法
    }

    @Test   //查詢單個  懶加載【延遲加載】
    @Transactional  //此包下import org.springframework.transaction.annotation.Transactional;
    public void getOneTest(){
        Student student=sd.getOne(3);
        System.out.println("-------分割線-------");
        System.out.println("打印對象:"+student);
        //控制台打印
        /*
            -------分割線-------
            Hibernate: select student0_.sid as sid1_0_0_, student0_.saddress as saddress2_0_0_,
            student0_.sage as sage3_0_0_, student0_.smoney as smoney4_0_0_, student0_.sname as
            sname5_0_0_ from student student0_ where student0_.sid=?
            打印對象:Student{id=3, name='李小二', age=23, money=6666.8, address='安徽大別山'}
         */
        //說明:在執行到getOne方法時並不會去數據庫查詢數據,而只是把此方法交給了代理對象,在後面
        //      某個代碼要對查詢的對象student操作時才會去數據庫做真正查詢
        //底層調用:EntityManager裏面的getReference方法
    }

getOne【延遲加載】/findOne【立即加載】

這裏說明一下:在使用延遲加載的時候必須在方法上面加事務註解

②:添加/更新操作

    @Test   //添加
    @Transactional  //開啟事務
    @Rollback(value = false)    //不自動回滾
    public void savetestA(){
        //注意這裏 我調用了一個不帶 ID的構咋函數
        Student student=new Student("蒼井空",25,555.6,"日本東京");
        Student save = sd.save(student);
        System.out.println("添加的數據是:"+save);
    }

    @Test   //更新
    public void updatetestA(){
        //更新操作的時候 首先要知道更新的ID 后通過把數據全部查詢出來,然後再通過set方法修改
        Student student = sd.findOne(5);    //查詢
        student.setName("小次郎");
        student.setAddress("日本");
        //更新
        Student save = sd.save(student);
        System.out.println("更新后的數據是:"+save);
    }
    /**
     * 總結:更新/添加都是save
     * 區別:傳入帶ID的屬性就是更新  不傳入ID屬性就是添加
     * 注意:添加必須添加事務和回滾機制
     */

更新/添加

   綜上總結:在了解了前面的一些簡單的增刪改查,它都是內部提供好的方法(前提實現指定接口),可以滿足我們開發的大部分需求,可是多表查詢、多條件查詢等等是系統沒有定義的,接下來我和大家介紹一下複雜的查詢及動態查詢等等

 三:SpringDataJPA的複雜自定義CRUD操作

 1:使用JPQL語法進行操作(寫接口方法

  其實我們在使用SpringDataJPA提供的方法就可以解決大部分需求了,但是對於某些業務來說是要有更複雜的查詢及操作,這裏我就要使用自定義接口和註解@Query來完成JPQL的一系列操作,關於JPQL操作我在JPA的一章已經說過大概了

//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {

    //查詢全部 根據年齡和地址查詢
    @Query("from Student where age=?1 and address=?2")
    List<Student> findByAgeAndAddress(Integer age,String address);

    //查詢全部 根據姓名模糊查詢
    @Query("from Student where name like ?1")
    List<Student> findByLikeName(String name);

    //查詢全部 根據地址為空的返回
    @Query("from Student where address is null ")
    List<Student> findByAddressIsNull();

    //查詢全部學生零花錢大於平均零花錢的所有學生
    @Query("from Student where money > (select avg(money) from Student)")
    List<Student> findByMoneyBigAvgMoney();

    //查詢零花錢在某個範圍內並且降序
    @Query("from Student where money between ?1 and ?2 order by money desc")
    List<Student> findByMoneyBetweenToOrderBy(Integer begin ,Integer end);

    //查詢全部個數
    @Query("select count(id) from Student")
    Long totalCount();

    /*********************刪改操作************************/
    //根據姓名刪除
    @Query("delete from Student where name=?1")
    @Modifying
    void deleteByName(String name);

    //更新數據
    @Query("update Student set name=?2 , address=?3 where id=?1")
    @Modifying
    void updateNameAndAddressById(Integer id,String name,String address);
}

使用JPQL自定義方法完成刪改查 【普通常用】

@Test
@Transactional
@Rollback(value = false)
public void test() {
    //裏面調用JPQL自定義的刪改操作
}
說明: 在使用@Query註解裏面定義的JPQL語法時 查詢不需要事務支持,但是刪改必須有事務及回滾操作,
   而且在定義JPQL語句下面要註明@Modify代表是刪改 JPQL 裏面不存在添加insert操作,所有大家要添加操作的話 調用原來存在的方法
//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {
    //使用排序   地址模糊查詢后 再按照id降序排列
    @Query("from Student where address like ?1 ")
    List<Student> findByLikeAddressOrderById(String address, Sort sort);

    //使用命名參數  查詢年齡和地址模糊查詢
    @Query("select s from Student as s where address like :address and age=:age")
    List<Student> findByLikeAddressAndAge(@Param(value="age") Integer s_age,@Param("address") String s_address);
}
/***下面測試方法****/

    @Test   //查詢學生帶排序
    public void test() {
        //說在前面:Sort如果不指定DESC或者ASC 如 new Sort("id");說明默認對id ASC升序排列
        //設置id為DESC降序排列
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        List<Student> students = sd.findByLikeAddressOrderById("%大%", sort);
        //打印略
    }
    @Test   //使用命名參數
    public void testA() {
        List<Student> students = sd.findByLikeAddressAndAge(23, "%大別山%");
        //打印略
    }

使用JPAL查詢 【其它查方式】

2:使用原生SQL語句進行操作(寫接口方法

  這種寫SQL語句的在SpringDataJPA中並不常用,因為SpringDataJPA對封裝性那麼高,而且是ORM思想,有各種方法及JPQL語句支持,所有我們本地SQL查詢也只要了解會用即可,SQL語句和我們平常寫的都一樣

//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {

    //查詢指定的年齡和地址
    @Query(value = "select * from student where sage=?2 and saddress=?1",nativeQuery = true)
    List<Student> findByAgeAndAddress(String address,Integer age);
}

3:方法命名規則查詢

  在Spring Data JPA裏面有一種很不錯的操作,那就是在接口中定義方法而不用實現也不用註解就可以實現其查詢操作,但是寫方法名查詢是有一套規範的,這裏我在官方文檔裏面整理出來了分享給大家【注意:方法名命名規則不能對其增加(insert)和修改(update)】

 ①:方法名查詢的具體規則

//示例場景 //當前域為Student 就是對Student上註解了@Entity,表示對於ORM思想它是一張表 //Student域屬性有name、age、address、dog //dog裏面又是一個封裝類 封裝了Dog的一些屬性如 name、color等

①:按照Spring data的定義規則,查詢的方法必須為【find/findBy/read/readBy/get/getBy】種方式開頭,後面則寫條件屬性關鍵字,條件屬性的
    首字母必須大寫,因為框架在進行方法名解析的時候,會把方法名前面多餘的前綴截取掉,然後對剩下的進行解析
例:
    截取前:getByNameAndAge  截取后:NameAndAge
    截取前:findByAddressLikeAndAge 截取后:AddressLikeAndAge
    截取前:findByDogName 截取后:DogName
問題:findByNameOrderByDesc 這種最後攜帶一個 OrderByDesc 排序的怎麼辦呢? 其實最後面攜帶這種對數據進行最後的排序等等操作,框架會對其另外拆分

②:對已經分割好的方法名如 ‘DogName’ ,根據POJO規範會把首字母變為小寫 ‘dogName’,然後用 ‘dogName’這個屬性去Student域里
    面查詢是否存在此屬性,如果是裏面的某個屬性則進行查詢。

③:如果查詢不到此屬性,則會對‘dogName’從右向左來截取,截取到從右向左的第一個大寫字母(所有截取到了Name),然後框架會把剩下
    的字符‘dog’去Student域裏面查詢,找到此屬性則查詢,沒有會重複前面的截取操作,(重要步驟)

④:現在框架提取到了到了‘dog’,然後去Student域查詢到了,則表示方法最終是根據Student.dog.name方式進行查詢,假設name屬性後面
    還是一個封裝類,框架會一直截取,根據③方式截取。

注意:在寫上面這類方法名是,而且查詢的裏面會有一層一層的封裝類,所有強烈建議使用findByDog_Name();

②:注意一些細節(必看)

1:查詢的條件為null時
    實體介紹:Student裡面包含 address為String類型 age為Integer類型 
    查詢方法定義:List<Student> findByAddressAndAge(String address, Integer age);
    調用時傳入值:List<Student> students = sd.findByAddressAndAge(null,23);
    後台生成的SQL:where (student0_.saddress is null) and student0_.sage=?
 
    結論:當我們傳入了一個null值時,表示會去查詢數據庫中address中字段為null的數據其實在編寫此代碼傳入null值以為會跳過此判斷,
         但是恰恰相反,框架會去查詢為null的數據。(實現此方式的只能去動態查詢,這種簡單的查詢不能滿足要求)

2:排序功能
    List<Student> findByAddressOrderByAge(String address); //名稱正序(正序時,推薦此方式,簡單)
    List<Student> findByAddressOrderByAgeAsc(String address); //名稱正序(效果同上)
    List<Student> findByAddressOrderByAgeDesc(String address); //名稱倒序

3:限定查詢結果集大小
    ①:Student findFirstByOrderByAgeAsc();
    ②:Student findTopByOrderByAgeAsc();
    說明:對錶的全部數據根據age進行Asc(升序)排序后再選擇第一條數據返回
    相應SQL:select .... from student student0_ order by student0_.sage asc limit ?  (這裡是limit 1)
    注意但是我如果寫出:List<Student> findTop3ByOrderByAgeAsc(); 則就是每次返回3條 limit 3
    
    ③:List<Student> findFirst2ByAddress(String address,Sort sort);
    ④:List<Student> findTop2ByAddress(String address,Sort sort);
    說明:首先進行數據查詢並通過Sort進行排序后 再篩選數據列表中最前面的2行數據返回 limit 2
    
    ⑤:Page<Student> queryFirst2ByAddress(String address, Pageable pageable)
    ⑥:List<Student> queryFirst2ByAddress(String address, Pageable pageable)
    說明:首先進行數據查詢 查詢全部指定的address後進行分頁,分頁完後進行數據控制
    控制說明:關於帶分頁的控制是,假設分頁過後查詢的數據id為3,4,5 查詢出這三條數據後進行數據控制,
              本案例是控製為2條,那返回的id數據就是3,4兩條記錄,id為5的就捨棄,那麼如果數據控制是5條,
              那麼就會打印3,4,5另外再去添加6,7補充數據長度
    
    總結:這裏的這裏的First和Top意思相同都是最前面,query也和find一樣;
    
    關於一個小點:
        Page<Student> students = sd.queryFirst1ByAddress("安徽大別山",new PageRequest(1,2));
        這裏我是返回分頁后的第一條數據,可是返回了分頁數據的前一個,分頁后id是3,4控制數據后First1
        返回了id為2 

4:計數 返回總數
    Long countByAddress(String address);

5:刪除
    void deleteByAddress(String address);
    //或 void removeByAddress(String address);
    說明:必須添加事務和回滾,這樣根據條件找到幾條就刪幾條
    @Test
    @Transactional
    @Rollback(value=false)
    public void test() {
        sd.deleteByAddress("安徽大別山");
    }
    //對應的SQL語句
    //Hibernate: select .... from student student0_ where student0_.saddress=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?
    //Hibernate: delete from student where sid=?

一些注意事項及其它操作

 ③:列舉幾個常用的方法定義查詢

//根據地址查詢後進行姓名模糊匹配
List<Student> findByAddressAndNameLike(String address ,String likeName);

//查詢指定範圍類的個數
List<Student> countByAgeBetween(Integer ... arg);

//根據對應的Address查詢后對查詢出的數據根據age排序后返回第一條數據
Student findFirstByAddressOrderByAgeDesc(String address);

 四:SpringDataJpa的源碼快速分析

   通過前幾節的增刪改查大家會發現,我們不用寫Dao接口實現類而是繼承JpaRepository和JpaSpecificationExcutor這兩個接口就能夠保證正常的增刪改查功能,這是為什麼呢?

//@Repository("studentDao") //這裏不用加入IOC容器 Spring默認幫我們注入
public interface StudentDao extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;   //這裏注入進去的數據又是誰呢?
    
    @Test
    public void testA(){
        //查詢單個
        Student one = sd.findOne(2);
        System.out.println(one);
    }
}

  其實不難想象,但凡是憑空就可以注入對象的,這內部肯定用到了JDK動態代理,那JDK代理類又是誰呢?經過debug發現是JdkDynamicAopProxy

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  ....//這個類實現了InvocationHandler 那必然有invoke方法,這裏面是操作該生成哪個代理對象返回 }

  所有說在那個注入的時候,Spring AOP就是通過JdkDyynamicAopProxy類幫我們實現的,那這個代理類最終創建的代理返回的對象又是誰呢?

   那我們再去查看一下SimpleJpaRepository類,肯定是實現了我們自己接口繼承的2個類,而且也有許多的操作方法的實現

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
  .....
}

總結:就是通過Jdk動態代理生成一個對象返回注入后,我們就可以調出各種操作方法

 五:SpringDataJpa動態查詢

  在日常編程中往往是查詢某個實體的時候,給定的條件是不固定的,那我們在不固定查詢條件的情況下就在Dao編寫查詢方法嗎?這顯然是不可取的,只要在確定及肯定會用到這個查詢條件后才會去Dao編寫此查詢方法;那麼在查詢不固定的情況下我們就會用到Specification動態查詢,要想使用動態查詢的話必須在當前使用的Dao接口下繼承JpaSpecificationExecutor接口;在使用動態查詢對比JPQL,其動態查詢是類型安全,更加面向對象。

/**
 * ①:JpaSpecificationExecutor:
 *  用來做動態查詢的接口
 * ②:Specification:
 *  是Spring Data JPA提供的一個查詢規範,要做複雜的查詢,只需圍繞這個規範來設置查詢條件。
 * JpaSpecificationExecutor接口下一共就5個接口方法
 * 有查詢單個、查詢全部、查詢全部【分頁】、查詢全部【排序】、統計總數
 */
public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> spec);
    List<T> findAll(Specification<T> spec);
    Page<T> findAll(Specification<T> spec, Pageable pageable);
    List<T> findAll(Specification<T> spec, Sort sort);
    long count(Specification<T> spec);
}

1:常用查詢(簡單)

①:Root<X> 接口
    此接口是代表查詢根對象,可以獲取實體類中的屬性
    如:root.get("name"); 獲取了實體類的name屬性

②:CriteriaBuilder 接口
    此接口用來構建查詢,此對象裏面有許多查詢條件方法
    方法名稱 Sql對應關係
    equle filed = value gt(greaterThan ) filed > value lt(lessThan ) filed < value ge(greaterThanOrEqualTo ) filed >= value le( lessThanOrEqualTo) filed <= value notEqule filed != value like filed like value notLike filed not like value 注:其實這裏面還有許多的查詢條件方法如求平均值啦查詢指定範圍等等

①:根據某個字段查詢單個數據

//查詢單個
@Test
public void testB() {
    // Specification:查詢條件設置
    Specification<Student> spe = new Specification<Student>() {
        //實現接口的方法   關於Root、CriteriaBuilder上面有介紹
        public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //獲取實體屬性name
            Path<Object> name = root.get("name");
            //根據指定的姓名查詢
            Predicate select = cb.equal(name, "螞蟻小哥");
            //返回一定是Predicate類型
            return select;
        }
    };
    //查詢單個
    Student student = sd.findOne(spe);
    System.out.println(student);
}

②:多條件查詢、模糊查詢等一些操作

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    @Test   //多條件查詢
    public void testC() {
        //根據年齡和地址查詢
        Specification<Student> spe=new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> age = root.get("age");
                Path<Object> address = root.get("address");
                Predicate p1 = cb.equal(age, "23");
                Predicate p2 = cb.equal(address, "安徽大別山");
                Predicate select = cb.and(p1, p2);
                return select;
            }
        };
        List<Student> students = sd.findAll();
        //打印略
    }

    @Test   //查詢多個條件
    public void testD(){
        //根據精準地址和模糊姓名查詢
        List<Student> students = sd.findAll(new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> address = root.get("address");
                Path<Object> name = root.get("name");
                Predicate p1 = cb.equal(address, "安徽大別山");
                Predicate p2 = cb.like(name.as(String.class), "李%");
                return cb.and(p1,p2);
            }
        });
        //注!!!:在使用gt、lt、ge、le、like等條件方法查詢的時候需要告知傳入的屬性的class屬性
        //如:cb.like(name.as(String.class), "李%");
        //打印略
    }

    @Test //查詢全部為指定住址的學生帶年齡排序
    public void testE(){
        List<Student> students = sd.findAll(new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.equal(root.get("address"), "安徽大別山");
            }
        }, new Sort(Sort.Direction.DESC, "age"));
    }
    
    @Test //查詢全部帶分頁
    public void testF(){
        Page<Student> students = sd.findAll(new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return null;    //return null 代表沒條件查詢全部
            }
        }, new PageRequest(2, 2));
    }
}

分頁、排序、多條件

2:複雜查詢(了解)

  我們在之前使用過了Root和CriteriaBuilder的2個接口,可是裏面還有一個參數,是CriteriaQuery接口,它代表一個頂層查詢條件,用來自定義查詢,操作頂層查詢可以使我們更靈活,但是在靈活的基礎上又多了一絲絲複雜

    @Test   //多條件查詢
    public void testC() {
        //根據年齡和地址查詢
        Specification<Student> spe=new Specification<Student>() {
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> age = root.get("age");
                Path<Object> address = root.get("address");
                Predicate p1 = cb.equal(age, "23");
                Predicate p2 = cb.equal(address, "安徽大別山");
                //query內部提供了一些連接用的如where 、having、groupBy....
                query.where(cb.and(p1,p2));
                return query.getRestriction();
            }
        };
        List<Student> students = sd.findAll();
        System.out.println(students);
        //打印略
    }

六:多表操作(一對一了解

補充:多表操作的 一對一、多對一(一對多)、多對多

1:確定多表之間的關係:
    一對一和一對多:
        一的一方稱為主表,而多的一方稱為從表,外鍵就要建立在從表上,它們的取值的來源主要來自主鍵
    多對多:
        這個時候需要建立一个中間表,中間表中最少由2個字段組成,這2個字段作為外鍵指向2張表的主鍵又組成了聯合主鍵

2:配置多表聯繫註解介紹
@OneToOne(一對一)
@ManyToOne(多對一)
@OneToMany(一對多)
@ManyToMany(多對多)
    內部參數:
        cascade:配置級聯操作
            CascadeType.ALL:所有
            CascadeType.MERGE:更新
            CascadeType.PERSIST:保存
            CascadeType.REMOVE:刪除
        fetch:配置延遲加載和立即加載
            FetchType.EAGER 立即加載
            FetchType.LAZY  延遲加載
        mappedBy:放棄外鍵維護

3:配置外鍵關係的註解
@JoinColumn(定義主鍵字段和外鍵字段的對應關係)
    內部參數:
        name:外鍵字段的名稱
        referencedColumnName:指定引用主表的主鍵字段名稱
        unique:是否唯一。默認值不唯一
        nullable:是否允許為空。默認值允許。
        insertable:是否允許插入。默認值允許。
        updatable:是否允許更新。默認值允許。
        columnDefinition:列的定義信息。
@JoinTable(針對中間表的設置)
    內部參數:
        name:配置中間表的名稱
        joinColumns:中間表的外鍵字段關聯當前實體類所對應表的主鍵字段
        inverseJoinColumn:中間表的外鍵字段關聯對方表的主鍵字段

  在多表操作中、一對一併不是我們掌握的重點,因為在開發中最多的還是使用一對多(多對一)和多對多,廢話不多說,我先來和大家說說一對一的具體操作步驟吧!

  我們在開始使用多表操作的時候,要對原來的部分代碼進行改造,建議大家搭建一個全新的項目,具體的代碼我已經在最上面的入門案例中給出了,只要在新項目中導入相應的坐標和配置文件即可,然後修改配置文件裏面的具體數據庫和數據庫密碼即可,還有就是現在的建表語句和上面的全部操作不太一樣,為了更好的演示一對一的操作我這邊準備了一個數據庫建表語句,下面的表含義是,每個學生表(student)都有一條家庭(family)的登記的記錄表,為一對一關係(理想化表,雙胞胎除外)

-- 一對一關係
-- 刪除庫
drop database demo_jpa_one_one;
-- 創建庫
create database if not exists demo_jpa_one_one charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa_one_one;

# 家庭表創建
create table if not exists family(
    fid int(11) primary key auto_increment, -- 家庭主鍵
    fmember int(11) not null,               -- 成員個數
    fguardian varchar(10) not null,         -- 監護人
    ftel char(11) not null,                 -- 監護人號碼          
    fdad varchar(10),                       -- 爸爸姓名
    fmom varchar(10),                       -- 媽媽姓名
    faddress varchar(20)                    -- 家庭詳細地址
)charset gbk; 
# 創建學生表
create table if not exists student (
    sid int(11) primary key auto_increment,             -- 編號
    sname varchar(5) not null,                          -- 姓名
    ssex enum('','') default '',                  -- 性別
    sage tinyint(11) unsigned not null default 20,      -- 年齡
    smoney decimal(4,1) default 0,                      -- 零花錢 最高999.9
    saddress varchar(10),                               -- 住址
    senrol date default '0000-00-00',                   -- 入學時間
    f_id int(11),                                       -- 連接家庭id
    foreign  key(f_id) references family(fid)           -- 連接家庭id主鍵
)charset gbk;
# 添加家庭信息
insert into family(fmember,fguardian,ftel,fdad,fmom,faddress)values
(0,"余蒙飄","","戚曦維","余蒙飄","安徽省六安市裕安區"),
(0,"孫戀烈","","梁輪亭","孫戀烈","安徽省合肥市瑤海區"),
(0,"張頻時","","張頻時","","安徽省安慶市宜秀區"),
(0,"王京正","","王京正","梁晝庭","安徽省六安市金安區"),
(0,"劉資納","","王諄斌","劉資納","安徽省滁州市全椒縣"),
(0,"白飛解","","廖旺賜","白飛解","安徽省安慶市大觀區"),
(0,"梁昀輝","","鄔國耿","梁昀輝","安徽省蚌埠市蚌山區"),
(0,"古錄鋼","","","古錄鋼","安徽省滁州市定遠縣"),
(0,"姬橋毅","","姬橋毅","寧竹熊","安徽省合肥市蜀山區"),
(0,"劉始瑛","","劉始瑛","韋歡億","安徽省淮南市大通區");
# 添加學生數據
insert into student(sid,sname,saddress,f_id)values
(1 ,"王生安","安徽六安",1 ),(2 ,"李鑫灝","安徽合肥",2 ),
(3 ,"薛佛世","安徽蚌埠",3 ),(4 ,"蔡壯保","安徽安慶",4 ),
(5 ,"錢勤堃","安徽合肥",5 ),(6 ,"潘恩依","安徽合肥",6 ),
(7 ,"陳國柏","安徽六安",7 ),(8 ,"魏皚虎","安徽六安",8 ),
(9 ,"周卓浩","安徽六安",9 ),(10,"湯辟邦","安徽六安",10);
-- 更新數據
update student set ssex=ceil(rand()*2),sage=ceil(rand()*5+20),smoney=(rand()*999),
senrol=concat(ceil(rand()*3+2017),'-' , ceil(rand()*12) , '-',ceil(rand()*20));
update family set ftel=concat(if(ceil(rand()*3)=1,if(ceil(rand()*3)=2,if(ceil(rand()*3)=1,
    if(ceil(rand()*3)=2,"155","166"),if(ceil(rand()*3)=3,"163","170")),"164"),
if(ceil(rand()*3)=3,"188","176")),floor(rand()*90000000+9999999)),fmember=ceil(rand()*3+2);
-- 查詢數據
show create table student\G
show create table family\G
select * from student;
select * from family;

一對一 數據庫建表語句

  其實我們並不需要創建數據庫表(數據庫必須創建),因為我們使用的是ORM思想的框架,說白了就是程序自動幫我們創建表(設置create配置),我們只需要關心實體類就可以了,這個實體類我也為大家準備好了

/**
 * 創建了一個Student的實體-表關係類
 */
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "ssex")
    private String sex;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    @Column(name = "senrol")
    private String enrol;
    //get/set/toString/無參有參構造器 大家自己創建
}

Student的Entity實體對象   未指定外鍵關係

/**
 * 創建了一個Family的實體-表關係類
 */
@Entity
@Table(name = "family")
public class Family {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "fid")
    private Integer id;
    @Column(name = "fmember")
    private Integer member;
    @Column(name = "fguardian")
    private String guardian;
    @Column(name = "ftel")
    private String tel;
    @Column(name = "fdad")
    private String dad;
    @Column(name = "fmom")
    private String mom;
    @Column(name = "faddress")
    private String address;
    //get/set/toString/無參有參構造器 大家自己創建
}

Family的Entity實體對象    未指定外鍵關係

  在準備好了映射關係之後,我們要創建StudentDao、FamilyDao這2個接口,並且分別繼承JpaRepository和JpaSpecificationExecutor這2個接口,完成了這些操作后就可以開始編寫測試類了

<!--大家把配置文件的JPA配置方式改為create每次都創建表-->
<property name="jpaProperties">
     <props>
          <prop key="hibernate.hbm2ddl.auto">create</prop>
     </props>
</property>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;
    @Autowired
    @Qualifier(value = "familyDao")
    private FamilyDao fd;

    @Test   //完成保存操作
    @Transactional
    @Rollback(value = false)
    public void testC() {
       Student stu=new Student("王二","男",26,666.3,"安徽六安","2020-6-5");
       Family fam=new Family(3,"王二牛","17688888888","王二牛","母老虎","安徽六安裕安區白嫖村");
       //開始保存
       sd.save(stu);
       fd.save(fam);
    }
}

如果不出意外,大家可能會遇到一個異常,這正是我想和大家說的,具體說明在文章最後的【關於多表操作異常】問題總結和大家說明一下,我們解決問題后回來再次運行后,控制台會打印2條建表語句和插入數據的語句,前面說過,這2張表是一個一對一的關聯關係,我們接下來就對這2張表建立關係。

建立關係 Student---Family (一對一) 為Student建立關聯到Family的關係(在Student中增加一個字段)
    @OneToOne(targetEntity = Family.class)
    @JoinColumn(name = "f_id", referencedColumnName = "fid")
    private Family family;
為Family建立關聯到Student的關係(在Family中增加一個字段)
    @OneToOne(targetEntity = Student.class)
    @JoinColumn(name = "f_id", referencedColumnName = "sid")
    private Student student;
這樣就完成了相互引用了,每個實體類都擁護一個對方實體,完成了一對一的關聯

完成了關聯后我們對測試方法進行改造

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        Student stu = new Student("王二", "男", 26, 666.3, "安徽六安", "2020-6-5");
        Family fam = new Family(3, "王二牛", "17688888888", "王二牛", "母老虎", "安徽六安裕安區白嫖村");
        //通過set方法分別設置對應的字段,使其關聯
 stu.setFamily(fam); fam.setStudent(stu);
        sd.save(stu);
        fd.save(fam);
    }

運行後會自動幫我們創建2張表並且設置外鍵

mysql> select * from student;
+-----+----------+------+----------+--------+-------+------+------+
| sid | saddress | sage | senrol   | smoney | sname | ssex | f_id |
+-----+----------+------+----------+--------+-------+------+------+
|   1 | 安徽六安  |   26  | 2020-6-5 |  666.3 | 王二  | 男   |  1   |
+-----+----------+------+----------+--------+-------+------+------+
mysql> select * from family;
+-----+----------------------+--------+-----------+---------+--------+-------------+------+
| fid | faddress             | fdad   | fguardian | fmember | fmom   | ftel        | f_id |
+-----+----------------------+--------+-----------+---------+--------+-------------+------+
|   1 | 安徽六安裕安區白嫖村   | 王二牛  | 王二牛     |       3 | 母老虎 | 17688888888  |   1  |
+-----+----------------------+--------+-----------+---------+--------+-------------+------+

  發現自動幫我們創建的表會有2個外鍵,可是這2個外鍵是多餘的,我們這個時候需要放棄外鍵維護,按照之前的一對一建表語句說明,外鍵是建立在學生表上的,通過學生表上面的外鍵來尋找到家庭表的信息,這個時候我們在家庭表上面放棄外鍵維護

//@OneToOne(targetEntity = Student.class)
    //@JoinColumn(name = "f_id", referencedColumnName = "sid")
    //引用對方的字段
    @OneToOne(mappedBy = "family")
    private Student student;

1:一對一的對象導航查詢

<property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <!--設置數據庫方言-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            </props>
        </property>
 @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        System.out.println(sd.findOne(1));
        //我們要編寫toString方法后才可以打印全部
    }

七:多表操作(一對多/多對一重點

  其實一對多和多對一本質上是一樣的,只是順序發生了變化,我就詳細的把一對多給講一下,在這裏我已經有過一個數據庫建表語句,但是因為是ORM項目,我們建表語句是由程序幫我們自動創建的,之所以我編寫了SQL語句是為了方便後期查詢練習

#### 一對多  這裏我使用的關係是一個老師對應多個學生,一個學生對應一個老師
#### 老師表為主表、學生表為從表
#### 一對多
-- 刪除庫
drop database demo_jpa_one_many;
-- 創建庫
create database if not exists demo_jpa_one_many charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa_one_many;
-- 創建主表 (輔導員表)
create table if not exists teacher(
    tid int primary key,               -- 編號
    tname varchar(5) not null,         -- 姓名
    tsex enum('','') default '', -- 性別
    tage tinyint unsigned,             -- 年齡
    tsalary decimal(6,1) default 0,    -- 工資 最高99999.9
    taddress varchar(10)               -- 住址
)charset gbk collate gbk_chinese_ci;

-- 創建從表 (學生表)
create table if not exists student (
    sid int(11) primary key auto_increment,             -- 編號
    sname varchar(5) not null,                          -- 姓名
    ssex enum('','') default '',                  -- 性別
    sage tinyint(11) unsigned not null default 20,      -- 年齡
    smoney decimal(4,1) default 0,                      -- 零花錢 最高999.9
    saddress varchar(10),                               -- 住址
    senrol date default '0000-00-00',                   -- 入學時間
    tid int ,                                           -- 連接老師id
    foreign  key(tid) references teacher(tid)  -- 連接老師主鍵
)charset gbk;

-- 添加老師數據
insert into teacher (tid,tname,taddress)values
(1,'張老師','江蘇南京'),
(2,'李老師','江蘇無錫'),
(3,'王老師','江蘇常熟');

-- 添加學生數據
insert into student(sid,sname,saddress)values
(1 ,"王生安","安徽六安"),(2 ,"李鑫灝","安徽合肥"),
(3 ,"薛佛世","安徽蚌埠"),(4 ,"蔡壯保","安徽安慶"),
(5 ,"錢勤堃","安徽合肥"),(6 ,"潘恩依","安徽合肥"),
(7 ,"陳國柏","安徽六安"),(8 ,"魏皚虎",null);

-- 數據更新
update teacher set tsex=ceil(rand()*2),tage=ceil(rand()*10+25),tsalary=ceil(rand()*3000+8000);
update student set ssex=ceil(rand()*2),sage=ceil(rand()*5+20),smoney=(rand()*999),
senrol=concat(ceil(rand()*3+2017),'-' , ceil(rand()*12) , '-',ceil(rand()*20)),tid=ceil(rand()*3);

一對多 關係建表建庫語句

  此時的一對多關係是【一個老師有多個學生】,反之,如果是多對一隻需要調換關係即可,2者差不多,我就針對這個一對多講解;在編寫程序之前我們必須要搞明白什麼是主表和從表的關係,這影響到我們後面編寫一對多的關係:

主表:
    由主鍵和其它字段組成,後期由其它表參照當前表主鍵,此時的表為主表,
    在其它表中如果引用主表的主鍵字段,那麼主表的主鍵被引用后將不能隨意更改,
    如果強制更改則必須設置級聯操作(cascade)
從表:
    由主鍵、外鍵和其它字段組成,後期由本表的外鍵字段去引用其它帶有主鍵的表,一旦其它的
    表主鍵被引用后,被引用的表則被稱為主表,反之引用其它表主鍵的表被稱之從表

一句話概括:有外鍵字段的表是從表(排除其它複雜表,因為有的表即引用其它別也被其它表引用)

  在我們了解了上面的關係后,我們就可以搭建操作一對多的環境了,首先我們就是要準備建造一個數據庫(不是數據表)名稱為demo_jpa_one_many,因為這個框架只要編寫好映射關係後會自動幫我們創建表的;現在建立一個項目,只需要在空項目導入pom.xml坐標和配置外鍵(配置文件一定要修改正確數據庫連接),我將帶大家一步一步完成一對多操作,如果這些都會直接跳過;完成了上面的操作后,我們首先就是要去編寫實體類及映射關係:

/**
 * 創建了一個Teacher的實體-表關係類
 * 在案例中 當前老師表為主表
 */
@Entity
@Table(name = "teacher")
public class Teacher {

    //創建主鍵字段和普通字段
    @Id
    @Column(name = "tid")
    private Integer id;
    @Column(name = "tname")
    private String name;
    @Column(name = "tsex")
    private String sex;
    @Column(name = "tage")
    private Integer age;
    @Column(name = "tsalary")
    private Double salary;
    @Column(name = "taddress")
    private String address;

    //省略 get/set/有參無參構造器
}

老師表映射關係 主表

/**
 * 創建了一個Student的實體-表關係類
 * 在案例中 當前學生表為從表
 */
@Entity
@Table(name = "student")
public class Student {

    //創建主鍵字段和普通字段
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "ssex")
    private String sex;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    @Column(name = "senrol")
    private String enrol;
    //省略 get/set/有參無參構造器
}

學生表映射關係 從表

  在完成上面的一些操作后,大家已經完成了實體類及映射的創建,這裏我沒有為這2張表建立外鍵關係,因為在沒接觸過多表操作的話冒然把映射一起寫完有可能會出現異常(肯定有人說,這咋這麼啰嗦呀)那好,對有操作的,只是複習怎麼使用的直接可以當cv攻城師複製即可;現在我們來編寫2個dao類

public interface StudentDao extends JpaRepository<Student,Integer>, JpaSpecificationExecutor<Student> { }
public interface TeacherDao extends JpaRepository<Teacher,Integer> , JpaSpecificationExecutor<Teacher> { }

  寫好以後就正式測試了,測試完成后大家會發現是2個沒有任何關係的2張獨立的表(如果出現異常了去下面查看異常講解

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;
    @Autowired
    @Qualifier(value="teacherDao")
    private TeacherDao td;

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        Student student=new Student("張小三","男",25,222.3,"安徽六安","2018-8-8");
        Teacher teacher=new Teacher(10,"張老師","男",35,9999.6,"北京順義");
        //保存
        td.save(teacher);
        sd.save(student);
    }
}

測試代碼 創建2張表

mysql> select * from student;
+-----+----------+------+----------+--------+--------+------+
| sid | saddress | sage | senrol   | smoney | sname  | ssex |
+-----+----------+------+----------+--------+--------+------+
|   1 | 安徽六安   |   25 | 2018-8-8 |  222.3 | 張小三 | 男   |
+-----+----------+------+----------+--------+--------+------+
mysql> select * from teacher;
+-----+----------+------+--------+---------+------+
| tid | taddress | tage | tname  | tsalary | tsex |
+-----+----------+------+--------+---------+------+
|  10 | 北京順義 |   35 | 張老師   |  9999.6 | 男    |
+-----+----------+------+--------+---------+------+

注:創建2表的聯繫

  在創建2表的關係中,我們必須要分清主表和從表的關係,這樣才可以設計出一個完整的關係創建,具體的主表和從表上面以給出介紹

創建從表的關係連接(student)
在從表中添加一個引用teacher屬性,因為一個student中有一個teacher
//創建外鍵字段 因為從表上有明確外鍵引用其它表 所以必須要有外鍵字段
@ManyToOne(targetEntity = Teacher.class) @JoinColumn(name="t_id",referencedColumnName = "tid") private Teacher teacher;
注:
    targetEntity = Teacher.class:當前引用的主表類型
    name="t_id" 代表當前student表中的外鍵字段
    referencedColumnName = "tid"  代表參照主表的哪個字段
建表樣式:
mysql> select * from student;
+-----+----------+------+----------+--------+--------+------+------+
| sid | saddress | sage | senrol   | smoney | sname  | ssex | t_id |
+-----+----------+------+----------+--------+--------+------+------+
|   1 | 安徽六安 |   25 | 2020-6-6 |  666.3 | 李小二 | 男   |  100 |
+-----+----------+------+----------+--------+--------+------+------+

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 創建主表的關係連接(teacher)
在主表中添加一個引用student的屬性,因為一個teacher中有多個student
//創建外鍵字段 這裡是主表,所以主動放棄外鍵維護
//@OneToMany(targetEntity = Student.class)
//@JoinColumn(name = "s_id",referencedColumnName = "sid")
//引用從的外鍵字段
@OneToMany(mappedBy = "teacher") private Set<Student> students=new HashSet<Student>();

注:
    mappedBy = "teacher" :代表參照對方表

假設主表不放棄外鍵維護就會出現下面情況:
mysql> select * from teacher;
+-----+----------+------+--------+---------+------+------+
| tid | taddress | tage | tname  | tsalary | tsex | s_id |
+-----+----------+------+--------+---------+------+------+
| 100 | 北京順義 |   32 | 王老師 |  6666.6 | 男   |   1  |
+-----+----------+------+--------+---------+------+------+
問題所在:這時候主表引用從表,而從表也引用主表,這顯然不是一個合格設計
解決后:在主表上設置的放棄外鍵維護,並參照從表的關係
mysql> select * from teacher;
+-----+----------+------+--------+---------+------+
| tid | taddress | tage | tname  | tsalary | tsex |
+-----+----------+------+--------+---------+------+
| 100 | 北京順義 |   32 | 王老師 |  6666.6 | 男   |
+-----+----------+------+--------+---------+------+

  創建測試,在測試的時候大家把配置文件的配置改為create,代表每次執行都會創建表

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {
    //注入數據
    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;
    @Autowired
    @Qualifier(value="teacherDao")
    private TeacherDao td;

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        //創建2個對象
        Student student=new Student("李小二","男",25,666.3,"安徽六安","2020-6-6");
        Teacher teacher=new Teacher(100,"王老師","男",32,6666.6,"北京順義");
        //把老師放入學生數據中  把學生數據放入老師表中
        student.setTeacher(teacher);
        teacher.getStudents().add(student);
        //保存 這裏注意一下,因為我的teacher主鍵不是自動生成 ,
        // 所以先保存teacher才可以保存student,因為teacher主鍵不是自動生成,直接先保存student會無法獲取teacher主鍵
        td.save(teacher);
        sd.save(student);
    }
}

測試方法

  然後會生成和我給出的創建sql語句生成的相同的字段,並且外鍵也是可以的,如果出現異常,大家檢查一下get/set/無參構造/有參構造,這裏的無參構造必須存在,否則真的會異常

1:級聯操作

  級聯操作分級聯刪除、級聯添加、級聯更新,如果設置了級聯操作就可以完成級聯操作,具體的在一對一上的補充介紹了

    @OneToMany(mappedBy = "teacher",cascade = CascadeType.ALL)
    private Set<Student> students=new HashSet<Student>();

  在主表上設置了級聯操作(ALL=全部都支持),只要保存teaccher就會級聯着保存student

    @Test   //多條件查詢
    @Transactional
    @Rollback(value = false)
    public void testC() {
        //創建2個對象
        Student student=new Student("李小二","男",25,666.3,"安徽六安","2020-6-6");
        Teacher teacher=new Teacher(100,"王老師","男",32,6666.6,"北京順義");
        //把老師放入學生數據中  把學生數據放入老師表中
        student.setTeacher(teacher);
        teacher.getStudents().add(student);
        //直接添加老師就可以保存雙方數據
        td.save(teacher);
    }

2:查詢數據

  這個時候我們導入之前的sql語句,然後把配置改為update,每次執行,有表則不創建表,

    @Test   //多條件查詢
    @Transactional //必須添加事務
    public void testC() {
        Teacher teacher = td.findOne(2);
        System.out.println(teacher);
    }

 八:多表操作(多對多)

  多對多是一個雙向關係,我首先來展示一下SQL語句

##### 多對多

-- 刪除庫
drop database demo_jpa_many_many;
-- 創建庫
create database if not exists demo_jpa_many_many charset gbk collate gbk_chinese_ci;
-- 使用庫
use demo_jpa_many_many;
-- 創建從表 (學生表)
create table if not exists student (
    sid int(11) primary key auto_increment,             -- 編號
    sname varchar(5) not null,                          -- 姓名
    ssex enum('','') default '',                  -- 性別
    sage tinyint(11) unsigned not null default 20,      -- 年齡
    smoney decimal(4,1) default 0,                      -- 零花錢 最高999.9
    saddress varchar(10),                               -- 住址
    senrol date default '0000-00-00'                    -- 入學時間
)charset gbk;

# 學生社團組織
create table if not exists organization(
    oid int(11) primary key auto_increment,  -- 社團主鍵id
    oname varchar(10) not null unique        -- 社團名稱
)charset gbk;
# 中間表 社團和學生對應多對多關係
create table if not exists student_organization(
    soid int(11) primary key auto_increment,         -- 中間表id
    s_id int(11),                                    -- 學生id
    o_id int(11),                                    -- 社團id
    foreign key(s_id) references student(sid),       -- 連接學生id
    foreign key(o_id) references organization(oid)   -- 連接社團id
)charset gbk;

# 添加學生社團組織學習
insert into organization (oname) values
("書法協會"),("法律協會"),("武術協會"),
("魔術社團"),("網球協會"),("啦啦隊團");
-- 添加學生數據
insert into student(sid,sname,saddress)values
(1 ,"王生安","安徽六安"),(2 ,"李鑫灝","安徽合肥"),
(3 ,"薛佛世","安徽蚌埠"),(4 ,"蔡壯保","安徽安慶"),
(5 ,"錢勤堃","安徽合肥"),(6 ,"潘恩依","安徽合肥"),
(7 ,"陳國柏","安徽六安"),(8 ,"魏皚虎",null);
# 添加學生和社團的中間表
insert into student_organization(s_id,o_id) values
(1,1),(1,3),(1,2),(1,6),(2,5),(2,1),(2,2),
(6,6),(6,2),(6,4),(6,3),(7,4),(7,2);

多對多建表語句

  上面多對多關係是一個學生可以加入多個社團(組織),一個組織可以有多名學生,間就構建成多對多關係,廢話不多說,直接上映射關係

在創建映射關係的時候我們得確定哪一方會放棄外鍵維護權,
在多對多的時候我們通常放棄的被動的一方



@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "sid")
    private Integer id;
    @Column(name = "sname")
    private String name;
    @Column(name = "ssex")
    private String sex;
    @Column(name = "sage")
    private Integer age;
    @Column(name = "smoney")
    private Double money;
    @Column(name = "saddress")
    private String address;
    @Column(name = "ssenrol")
    private String senrol;

//    @ManyToMany(targetEntity = Organization.class)
//    @JoinTable(name = "student_organization",
//            joinColumns = {@JoinColumn(name = "s_id", referencedColumnName = "sid")} ,
//            inverseJoinColumns = {@JoinColumn(name = "o_id", referencedColumnName = "oid")}
//    )
    @ManyToMany(mappedBy = "students")
    private Set<Organization> organizations = new HashSet<Organization>();
}


@Entity
@Table(name = "organization")
public class Organization {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "oid")
    private Integer id;
    @Column(name = "oname")
    private String name;

    @ManyToMany(targetEntity = Student.class)
    @JoinTable(name = "student_organization",
            joinColumns ={@JoinColumn(name = "o_id",referencedColumnName = "oid")} ,
            inverseJoinColumns = {@JoinColumn(name = "s_id",referencedColumnName = "sid")}
    )
    private Set<Student> students=new HashSet<Student>();
}

多表映射關係

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Client {

    @Autowired
    @Qualifier(value = "studentDao")
    private StudentDao sd;

    @Autowired
    @Qualifier(value = "organizationDao")
    private OrganizationDao od;

    @Test
    @Transactional
    @Rollback(value = false)
    public void fun() {
        Student student=new Student("張三","男",45,555.6,"安徽六安","2012-8-8");
        Organization o=new Organization("魔術社");
        student.getOrganizations().add(o);
        o.getStudents().add(student);
        od.save(o);
        sd.save(student);
    }

}

測試環境

九:對象導航查詢

  在一對多查詢的時候,多的一方數據會有延遲加載,但是對於一對一查詢的時候數據會有立即加載

十:關於多表操作異常

1:關於hibernate數據庫方言問題(dialect)

6月 03, 2020 4:57:00 下午 org.hibernate.dialect.Dialect <init> INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
省去部分...
Hibernate: create table family (...) type=MyISAM
//上面一局為我們創建的是一張表並設置MyISAM引擎 錯誤就在這 無法運行了
6月 03, 2020 4:57:01 下午 org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl handleException WARN: GenerationTarget encountered exception accepting command : Error executing DDL "create table family (...) type=MyISAM" via JDBC Statement org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table family (...) type=MyISAM" via JDBC Statement //從上面錯誤可以看出 程序運行的時候默認的數據庫方言設置了 org.hibernate.dialect.MySQLDialect 而這個默認是MyISAM引擎

  問題所在:因為我導入的hibernate坐標是5.4.10.Final,在導入這類高版本的坐標往往要為數據庫方言設置MySQL5InnoDBDialect的配置,在我前面也測試了,關於坐標版本問題,發現5.0.x.Final左右的版本不用設置數據庫方言,默認即可。

<property name="jpaProperties">
   <props>
     <prop key="hibernate.hbm2ddl.auto">create</prop>
     <!--設置數據庫方言--> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
   </props>
</property>

  具體的版本在創建數據庫表的時候會拋各種異常,這裏我整理了一下數據庫方言,方便大家參考

<property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                <!--設置數據庫方言-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            </props>
        </property>

<!--數據庫方言-->
DB2                         org.hibernate.dialect.DB2Dialect
DB2 AS/400                  org.hibernate.dialect.DB2400Dialect
DB2 OS390                   org.hibernate.dialect.DB2390Dialect
PostgreSQL                  org.hibernate.dialect.PostgreSQLDialect
MySQL                       org.hibernate.dialect.MySQLDialect
MySQL with InnoDB           org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM           org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version)        org.hibernate.dialect.OracleDialect
Oracle 9i/10g               org.hibernate.dialect.Oracle9Dialect
Sybase                      org.hibernate.dialect.SybaseDialect
Sybase Anywhere             org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server        org.hibernate.dialect.SQLServerDialect
SAP DB                      org.hibernate.dialect.SAPDBDialect
Informix                    org.hibernate.dialect.InformixDialect
HypersonicSQL               org.hibernate.dialect.HSQLDialect
Ingres                      org.hibernate.dialect.IngresDialect
Progress                    org.hibernate.dialect.ProgressDialect
Mckoi SQL                   org.hibernate.dialect.MckoiDialect
Interbase                   org.hibernate.dialect.InterbaseDialect
Pointbase                   org.hibernate.dialect.PointbaseDialect
FrontBase                   org.hibernate.dialect.FrontbaseDialect
Firebird                    org.hibernate.dialect.FirebirdDialect

數據庫方言 hibernate.dialect

2:關於對象導航查詢異常

WARNING: Please consider reporting this to the maintainers of org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release
Hibernate: sql語句
java.lang.StackOverflowError at java.base/java.lang.AbstractStringBuilder.inflate(AbstractStringBuilder.java:202)
    at java.base/java.lang.AbstractStringBuilder.putStringAt(AbstractStringBuilder.java:1639)
    at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:513)
   .......

  關以這種錯誤大多都是編寫的toString語句發生了錯誤,大家在寫toString的時候一定要分清主表和從表,在編寫主表的toString的時候一定要去除外鍵字段,而在編寫從表的時候一定要加上外鍵字段,因為平時我們都是通過從表查詢數據(因為從表有指向主表的外鍵),這樣可以把主表的數據通過外鍵查詢出來,但是主表上如果也有從表的字段的話就會一隻循環,數據沒完沒了,所有拋異常也是對的,

總結一句話,從表有主表的字段,主表有從表的字段,打印從表順帶打印主表,但是主表裡面還有從表字段,然後繼續打印從表字段…….

 3:關於延遲加載事務問題

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.xw.domain.Teacher.students, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606) at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:327) at java.base/java.lang.String.valueOf(String.java:2801) at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)

  關於在使用延遲加載的時候,在當前的方法上必須設置@Transactional,因為在使用延遲加載底層已經使用了事務的相關方法

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

震驚!當Python遇到Excel后,將開啟你的認知蟲洞_潭子電動車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

本文主要內容:   1. Excel,你為什麼如此強大 2. 軟件開發也需要團隊作戰 3. Excel的集成方案演化 4. macOS特有的集成方案:applescript 5. Python與Excel集成,有哪些好處 6. 了解多個技術領域,可以間接提高自己的IQ 7. 提高開發效率的利器:生態滲透 8. 上代碼:Python到底如何與Excel交互 9. 用Python替代VBA  
本文主要講Python與Excel的關係以及集成方案,Office家族的其他成員,如Word、PowerPoint與Excel擁有類似的功能,Python同樣可以與Word、PowerPoint等Office成員結合,這些內容我以後會寫文章講解。   我相信看這篇文章的大多數人都是程序員。在廣大程序員的眼裡,Excel以及Office家族的其他成員完全不能與Python、Java、Sprint Boot這些技術相比。Office被貼上的標籤就是:非專業人員的工具。是由那些體制內的人、會計、業務人員使用的工具,其實這是對Excel的最大誤解。

 1. Excel,你為什麼如此強大

  Excel從一問世開始,定位就是橫跨辦公和開發兩界。在上個世界末推出的Excel5.0(那時還沒有office這個產品套件)就已經支持VBA了,那可是在Win32下(一種基於dos的shell,並不屬於真正的操作系統)。儘管有些原始(以現在的眼光看),但在當時卻顯得極為先進。因為Excel(以及同期的Word)是人類史上第一個內置領域特定語言(DSL)的民用系統(可以被各個領域的人使用的系統)。正是因為有了VBA,Excel才顯得非常強大。   可能有的程序員會問,即使Excel支持VBA,可以進行編程,也只不過是完成一些自動化操作而已。VBA的功能完全不能與Python、Java、C#、C++這些被廣大程序員熟知的編程語言相比,更別說成為編程領域的主流了。沒錯,如果單憑VBA本身的確無法與這些流行的編程語言相比,但要知道,VBA和Excel本身可以與其他開發工具融合,也就是說,可以讓VBA和Excel成為自己系統的一部分,這就顯得非常牛逼了。

 2. 軟件開發也需要團隊作戰

  那麼為什麼要將Excel與其他系統結合呢?其實在我們創業時、在公司完成一個項目時,甚至在參加籃球比賽時,都會高頻率提到一個詞:團隊。 現在也流行着一句話:沒有完美的個人,只有完美的團隊。也就是說,沒有人可以什麼都會,什麼都擅長,就算是天才達芬奇也是如此。但團隊就不一樣了,即使某項工作團隊里沒人能做,也可以擴充團隊成員,吸引能做這項工作的人加入團隊。   其實在開發軟件的過程中也同樣是講究團隊作戰的。不管我們使用的開發工具有多強大,用戶社區由多龐大,都無法規避一個事實:任何技術都不可能適合完成所有的工作。 當然,還有另外一種說法就是儘管某種技術可以完成某項工作,但並不是最好的選擇。   現在回到Excel上來。首先要知道Excel擅長什麼,對,沒錯,Excel擅長表格和圖表的製作。儘管有非常多的開發工具也可以製作表格和圖表,但很顯然,Excel是其中最好的(主要用門檻、易用性、功能和用戶基數的綜合指標來衡量)。所以,如果某一個系統要求將數據轉換為表格和圖表,那麼這項任務交給Excel來完成是再合適不過了。  
3. Excel的集成方案演化   在不同的時期,Excel的集成方案也不盡相同。在Excel的大眼夾時代(那時還是Office97和Office2000的時代),微軟與開源世界還水火不容,我也還處於學生時代。在那個時代,Excel只能與Windows下的開發工具融合,其中主要的開發工具包括微軟的VC、VB、以及Borland(現在已經消失)的Delphi、C++ Builder等。     最初的集成方案有如下3種:

  • 可視方式:通過OLE組件將Excel直接嵌入窗口中,其實就相當於擁有了一個高級版的表格編輯器
  • VBA方式:通過ActiveX技術創建Excel.Applicaiton對象(一般是CreateObject函數),然後就可以任意調用VBA的API了,我比較喜歡這種方式。通過這種方式,可以將系統中的數據按着一定的格式直接傳輸到Excel中,給用戶提交的是包含表格數據的Excel文檔。
  • 反客為主方式:這種方式將Excel作為主體。也就是說,主要的操作界面是Excel,在Excel中調用或訪問其他系統。例如,很多年前我做過一個基於Excel的報表系統。該系統分別使用Delphi和Excel實現。Delphi做的管理系統,將數據發送給Excel。但需要用戶自己調整報表格式。我採用的方案是通過Excel的VBA實現表格的格式設置。其中有部分功能需要訪問SQL Server數據庫,以及完成與Delphi實現的系統類似的功能,這部分功能使用了Delphi封裝的Dll(COM組件),然後通過Excel反過來調用這些DLL。最終用戶使用的方式是用主系統完成大部分工作。如果想調整Excel的報表樣式,可以直接用Excel來完成(會在Excel上提供一些自定義的菜單和按鈕,現在通過加載項【Add-ins】實現)

  不過隨着時間的推移,現在的微軟已經擁抱開源和其他系統。所以Excel也不僅限於Windows。在macOS上也可以完成在Windows上的大多數工作,甚至Visual Studio也推出了macOS版本。而且.net core也可以同時跨Windows、macOS和Linux三個平台,SQL Server也開始支持Linux。以後我們會在更多的場景看到微軟的身影。   既然Excel已經支持了macOS平台,就需要採用跨平台的方式與Excel集成。當然,前面介紹的幾種方案現在仍然可用,但僅限於Windows平台。不過我們無法預測用戶到底使用哪一個平台,所以應該盡量使用跨平台方案。   目前主要的跨平台方案有如下3種:

  • 直接修改xlsx文件
  • 通過VBA間接調用其他編程語言
  • 使用office.js

  第1中方式有很多編程語言都支持,例如,Python、Java、Julia、Go、JavaScript。幾乎你能想到的編程語言,都有支持xlsx格式的庫。這裏只討論Python。如果想了解更多關於集成Excel的技術,可以關注我的公眾號:極客起源。   在Python語言中,支持Excel文件格式的庫非常多,如非常著名的openpyxl、xlsxwriter等。通過這些庫,可以在不依賴Excel環境的情況下,生成xlsx格式的文件。不過這些庫基本是只是生成Excel文件,並不能更好地利用VBA以及更高級的功能。由於Excel文件格式非常複雜,完全支持比較困難,所以這些庫只是支持一部分Excel的功能,但這些功能對於絕大多數需求已經足夠了。   第2種其實是一種取巧的方式,通過VBA做橋,調用其他編程語言,相當於用其他編程語言代替了VBA。這其中典型的就是xlwings。儘管這種方式從表面上看可以直接在Excel中像使用VBA一樣使用這些編程語言,但從本質上看仍然是直接寫xlsx文件。功能其實與第1種方式相同,因為這並不是官方支持的功能。   第3種是office.js,這是微軟官方提供的一個基於JavaScript的程序庫。基於Node.js,可以用JavaScript完全取代VBA實現Excel以及Office其他成員的加載項。office.js可以在Electron、Web應用以及大多數基於JavaScript的場景中使用。關於office.js的內容我以後會寫文章詳細描述,對office.js感興趣的同學也可以關注“極客起源”公眾號,會不定期更新這方面的內容。    
4. macOS特有的集成方案:applescript 除了跨平台解決方案外,在macOS上,還支持使用applescript與Office(Excel、World、PowerPoint等)交互,這些內容我以後再撰寫文章詳細講解。如果要了解excel applescript api,可以參考Excel Reference。從效果來看,applescript操作Office與在Windows下通過COM組件操作Office類似,可以完全控制Office,只是applescript的語法更接近自然語言。   下面的applescript代碼會創建一個新的Excel文檔,並將其保存為first.xlsx文件。  

 

 運行后,會看到一個打開的Excel文檔,並且已經保存為firstx.xlsx文件。

 

 5. Python與Excel集成,有哪些好處

  Excel對於Python來說,可以將Excel看做是一個可編程的大組件。這個組件的主要功能就是可以製作任意複雜的報表和圖表。儘管Python有很多模塊可以製作報表和圖表。但這些模塊的功能和效果完全沒辦法與Excel相比。所以將Python與Excel結合的最大好處是可以快速完成製作報表的任務,而且效果杠杠滴。   將Python與Excel相結合,其實還會引出另外一個思考,就是成為專家還是通才的問題。我聽到有很多程序員說,要將某種語言搞通,如PHP、Python、Java等,然後就可以很輕鬆解決所有的問題。結果真是這樣嗎?   很久以前,我聽過一個關於微軟的故事(相信很多人也聽過),在微軟有一個幾十人的團隊,花了好幾個月還沒完成一個項目,聽說是遇到了某些難題。這時有一個老程序員(據說至少50歲以上)將自己關在辦公室里一個星期,搞定! 我們先不管這個故事是真是假,那麼從理論上來說,是否有這個可能呢?其實如果光看編程速度,再牛叉的程序員,也不可能比普通程序員快幾十倍,更何況數百倍了。但還有另外一種可能,就是這名老程序員使用了完全不同的方法,繞過了大多數影響效率的因素,例如,使用了不同的工具,採用了不同的轉換方式,甚至使用了不同的設計理念等等。這就不是能力問題了,而是認知的問題。我將其稱為“認知蟲洞”。

 所謂“認知蟲洞”,是指通過某種方式很難完成某項工作,但通過另外完全不同,甚至是顛覆三觀的方式,可以用極短的時間達到目的,而且效果極好。就像找到了可以穿越浩瀚星空的蟲洞。這也有點像數學中的“等價替換”。

  可能這個微軟的例子離我們太遠,下面舉一個我自己的關於Excel例子,很多年前,我還在國內某大型軟件公司作高級程序員。團隊需要製作大量的報表,使用的主要開發工具是Delphi、後端是SQL Server數據庫。Delphi本身有自己的報表系統,叫QuickReport。功能是很強大的,但問題是,做起來太費勁。例如,要畫表格線時,如果一不小心將某根線拖到了別的地方,而且被其他東西覆蓋,那你就找把,還必須要找到,否則打印出來的表格上就會莫名其妙多了根線。結果團隊好幾個人弄了好幾天還沒弄完(也包括我)。後來我實在不想這麼弄了(因為買了幾張影碟,着急回家看電影,不想加班),於是想到了利用Excel或Word來完成這個報表系統。通過Delphi傳輸數據。   說干就干,花了不到2個小時,所有的報表全部搞定(只有我一個人哦),幾個人幾天都沒搞定的東西,我自己不到2個小時搞定,這當然不是我編程速度快了幾十倍,而是處在了不同的維度,使用了完全不同的技術來實現,用QuickReport需要一根線一根線的畫,而使用Excel,我不需要畫線,只需要用SQL語句查詢出數據,然後將這些數據發送給Excel即可。單單用了一個Excel,速度就提高了這麼多,如果系統中很多部分都使用了類似的技術,那麼編程效率提高數百倍,甚至上千倍,也不是沒有可能的。  
6. 了解多個技術領域,可以間接提高自己的IQ   可能有的同學會問,既然可以將多種技術結合起來大幅度提高開發效率,那麼為什麼不通過團隊合作的方式來完成了,通常一個人無法學會那麼多技術。其實這就是一個認知的問題,團隊合作只有在項目所採用的技術被確定后,例如,如何集成多種技術,才可以發揮作用。問題是,如果多種知識分散在不同人的大腦中,很可能沒有人意識到應該去這樣融合多種技術,就更談不上團隊合作了,就像你要探索宇宙,首先你要知道存在宇宙這種東西,否則怎麼去探索呢? 我將這種現象稱為“認知孤島”(相對於“認知蟲洞”而言),就是說並不是沒有能力去做,而是壓根就沒有意識到應該這樣做(由於知識的缺乏、同時導致想象力的受限)。   為什麼達芬奇那麼牛逼,除了聰明之外,達芬奇還橫跨多個完全不同的領域,正是因為對生物學和解刨學的了解,蒙娜麗莎的畫像才會那麼自然,栩栩如生,因為達芬奇對骨骼、肌肉的構造非常了解,這是其他任何畫家都無法比肩的。如果你在某一個領域排名前20%,而在另外一個領域排名也是前20%,那麼如果需要兩個領域的知識來解決問題時,你就會排名前4%(20% * 20%),如果是3個領域就是百里挑一。如果是6個領域,那就是萬里挑一,據說達芬奇涉足十多個領域。由於自己和自己溝通的成本為0,所以只有擁有足夠多的知識,並且有融合他們的能力,那麼你就是下一個達芬奇!

 我們可以舉個數學與Python的例子:

 如果我們的程序需要計算某個表達式的定積分(例如y = 2 * x從0到1的定積分)。    假設我們使用的是Python語言,並且不太清楚有什麼庫可以自動計算定積分,那麼採用的方式就是利用數值計算的方式寫程序去完成,計算定積分的數值計算公式比較複雜,可能不是在短時間內能完成的。不過要是了解sympy這個庫,那就是幾行代碼的事:  

import sympy
x = sympy.Symbol('x')
f = 2 * x
# 開始計算定積分
print(sympy.integrate(f,(x,0,1)))

是不是很簡單呢? 只需要了解一個API的用法就搞定了。

假設現在我們還有一個需求,要計算某個函數在某一點的導數(導數在深度學習中經常使用),而手頭又沒有必要的庫(也有可能是不知道),那麼只要了解導數的原理,就很容易通過幾行代碼搞定,這就屬於高等數學的範疇了。  

 導數原理,右側是導數的計算公式,

# 計算導數的函數
def derivative(f,x):
  h = 0.0001   # x軸的增量,需要是一個很小的值,但要在浮點數精度範圍內,通常不能超過小數點后6位
  return (f(x + h) - f(x - h)) / (2 * h)
# 待計算的函數1(y = 2 * x)  
def f1(x):
    return 2 * x
import math
# 待計算的函數2(  y = sin(x) * cos(x) / (sin(x) + cos(x))  )
def f2(x):
    return math.sin(x) * math.cos(x) / (math.sin(x) + math.cos(x))

print(derivative(f1,10))    # 1.9999999999953388
print(derivative(f2,123))   # -0.331842825692652

   PS:對這些代碼和高等數學不熟悉也沒關係,這裏我只是舉個例子,後期我會寫一些關於數學和編程方面的文章,詳細解釋這些好玩的東西,可以關注我的公眾號:極客起源 ,會不斷更新各種技術和數學文章,以及視頻課程。   從這兩個案例可以看出,在某一個領域需要非常費勁才能搞出來的東西,在另一個領域其實就是hello world。如果了解足夠多的領域,那麼完成很多工作,就會表現出天才的特徵(這也是成為天才的途徑之一,另一個途徑是投胎)。  

 PS:Excel的功能不僅僅是製作報表,Excel還擁有強大的數據分析能力。所以如果將Python與Excel集成,就意味着Python將擁有了Excel的全部能力,相當於Python擁有了Excel的整個生態。我將其稱為“生態滲透”。也就是通過集成或其他方式,一種技術可以直接或間接使用另外一種技術的全部或大部分資源。

7. 提高開發效率的利器:生態滲透    在未來,支持生態滲透的開發方式會非常普遍,如果只是用了一些現成的庫或開源軟件,並不能大幅度提高開發效率,但如果可以利用某些強大系統的生態,就不一樣了。在未來,還會有很多支持“生態滲透”的開發工具。例如,我們團隊研發的UnityMarvel,就是一款超平台開發系統。這裏之所以稱為“超平台”,而不是“跨平台”,是因為UnityMarvel不僅僅可以跨操作系統平台,還可以跨數據庫平台,雲平台、API平台、開源硬件、物聯網等,以及支持虛擬SQL、客戶端服務端一體化、柔性熱更新、Office加載項、瀏覽器插件等新特性。因此稱為“超平台”開發系統。並且自己研發了Ori編程語言(語法融合了Python、Java、Go等語言的優秀特性,但功能得到了前所未有的增強)。通過這些特性,可以用前所未有的規模利用其他系統的生態,要遠比Python使用Excel的生態更完美。其他功能先不解釋(等發布后我再寫文章詳談),先說說UnityMarvel是如何跨數據庫的。   所謂跨數據庫(目前指關係型數據, 以後會支持文檔、鍵值等NoSQL數據庫),是指用UnityMarvel開發基於數據庫的應用並不需要事先確定到底用什麼數據庫(如MySQL、SQL Server、Oracle等),UnityMarvel內置了一種虛擬數據庫,可以直接用虛擬數據庫開發,在發布時,會要求選擇使用的數據庫,例如,選擇MySQL或SQL Server。UnityMarvel會通過rosetta引擎將Ori語言的代碼轉換為支持MySQL的代碼。關於數據庫的部分,主要是用過內置的一種虛擬SQL完成的,而且這種SQL語言是與Ori語言是融為一體的。例如,如果要從persons表中查詢出id大於30的所有記錄,可以直接這樣寫:  

var  result = SELECT * FROM persons
                                 WHERE id > 30;

  result的類型是SQLSelect,將result賦給Grid組件,就會直接显示查詢結果。當發布時,會將上面的代碼轉換為使用相應數據庫(如MySQL、SQL Server)的特定編程語言(如JavaScript、Java等)的代碼。這麼做的好處如下: 1. 不需要進行數據庫選型,數據庫是在發布時後期綁定的; 2. 如果想切換數據庫(例如,從MySQL換成Oracle),只需要重新發布,選擇相應的數據庫就可以了,不需要修改一行代碼; 3. 統一數據庫接口,開發人員並不需要了解各種數據庫的細節,開發門檻低; 4. UI與數據庫交互非常容易,不必考慮各種數據庫引擎和庫,只需要直接將SQL語句賦給與其交互的UI組件即可; 5. 自動檢測和去除大多數SQL中的潛在風險,如SQL注入等; 6. 脫離數據庫環境開發。例如,想使用MySQL數據庫開發,但當前機器上並沒有MySQL開發環境,又不想安裝MySQL。這時仍然可以用UnityMarvel內置的虛擬數據庫進行開發,然後發布即可,部署在有MySQL環境的機器上就可以成功運行了;  

 

 

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

8. 上代碼:Python到底如何與Excel交互   在這一節玩點真格的,看一看Python到底如何與Excel交互。我們使用目前最常用的openpyxl來完成操作。先看一個未處理的Excel表格。

 

 

 上面這個表格是關於營業計劃的數據,看起來很彆扭,因為不同層次的數據之間沒有縮進,也沒有背景顏色,甚至沒有表格線。如果要將這個表格交給領導,估計領導會拿起塊磚頭砸過來!   領導最希望見到下面的表格:

 這個表格看起來是不是很舒服呢!其實這個表格用Excel做起來也並不費勁。不過仍然需要N步,這裏就不詳細解釋如何用Excel來做這個表格了,現在來看如何利用Python閃電般進行格式轉換。

from openpyxl.styles import Alignment,Font,Border,Side,Color,PatternFill
import openpyxl
# 打開待轉換的文件
workbook = openpyxl.load_workbook('原始表格.xlsx')

ws = workbook.active
table = ws['A1':'D11']
# 設置字體
ft = Font(name="黑體")
for rows in table:
    for cell in rows:
        cell.font = ft
# 調整行高(所有的行的高度統一設置為18)
for i in range(1,ws.max_row + 1):
    ws.row_dimensions[i].height = 18.0
# 插入列
ws.insert_cols(1,1)

# 調整新插入列的寬度
ws.column_dimensions['A'].width = 5

# 插入行
ws.insert_rows(1,1)
# 調整新插入行的高度
ws.row_dimensions[1].height = 10


# 文字左對齊,数字右對齊
leftAlign = Alignment(horizontal='left',vertical='center')
rightAlign = Alignment(horizontal='right',vertical='center')
for row in ws.rows:
    for cell in row:
        # 数字,右對齊
        if type(cell.value) == int:
            cell.alignment = rightAlign
        else:
            cell.alignment = leftAlign

# 調整列寬
ws.column_dimensions['B'].width =20
ws.column_dimensions['C'].width =9
ws.column_dimensions['D'].width =9
ws.column_dimensions['E'].width =9
ws.column_dimensions['F'].width =4  # 讓表格線出來一點



# 為表格添加邊框

topBorder = Border(top=Side(border_style='thick',color='000000'))
                #bottom=Side(border_style='thick',color='000000'),
                #horizontal = Side(border_style='thin',color='000000'))
# 添加表格頂邊的粗線(包括最後沒有數據的列)
for col in range(2, ws.max_column + 2):
    ws.cell(2, col).border = topBorder

bottomBorder = Border(bottom=Side(border_style='thick',color='000000'))
# 添加表格底邊的粗線(包括最後沒有數據的列),這裏需要加1,是因為前面繪製表格頂邊的粗線時,最大列的數量已經多了1個
# 所以只需要加1即可
for col in range(2, ws.max_column + 1):
    ws.cell(ws.max_row, col).border = bottomBorder

# 添加水平細線
horizontalBorder = Border(top=Side(border_style='thin',color='000000'))
for col in range(2, ws.max_column + 1):
    for row in range(4,ws.max_row):
        ws.cell(row, col).border = horizontalBorder

# 單元格縮進
ws['B5'].alignment=Alignment(indent=1)
ws['B6'].alignment=Alignment(indent=1)
ws['B8'].alignment=Alignment(indent=1)
ws['B11'].alignment=Alignment(indent=1)

ws['B9'].alignment=Alignment(indent=2)
ws['B10'].alignment=Alignment(indent=2)

# 設置文字顏色
blueFont = Font(name="Arial",color = '4169E1')
for col in range(3, ws.max_column + 1):
    ws.cell(5, col).font= blueFont
    ws.cell(6, col).font = blueFont
    ws.cell(9, col).font = blueFont
    ws.cell(10, col).font = blueFont
# 設置背景色
fill = PatternFill("solid", fgColor="B0C4DE")
for col in range(2, ws.max_column + 1):
    ws.cell(4, col).fill= fill
    ws.cell(7, col).fill = fill
    ws.cell(12, col).fill = fill

workbook.save('轉換后的表格.xlsx')

print('見證奇迹的時刻')

 

現在運行程序,當輸出“見證奇迹的時刻”后,就會在當前目錄生成一個“轉換后的表格.xlsx”,該文件就是上圖的效果,是不是很神奇呢?   從這段程序中可以看出,轉換該表格需要多少步,代碼並不複雜,大家可以根據openpyxl的文檔研究。  
9. 用Python替代VBA   目前微軟官方還沒有將Python作為VBA的替代品,倒是將JavaScript作為了另外一個選擇(office.js),不過可以利用xlwings做一個折中。xlwings可以單獨使用,也可以通過xlwings office加載項提供的RunPython函數運行Python代碼。   現在有一個hello.py文件,代碼如下:  

import xlwings as xw
def hello_xlwings():
    wb = xw.Book.caller()
    wb.sheets[0].range("A1").value = "Hello xlwings!"

  在xlwings的安裝目錄有一個xlwings.xlam文件,該文件是Excel的加載項文件,也就是Excel VBA的發行包文件。現在隨便開啟一個空的Excel workbook,然後點擊“工具”>“Excel加載項”菜單項,會打開如下圖所示的對話框,找到xlwings.xlam文件,並選中該文件。

 

 然後在“開發工具”選項卡中點擊“Visual Basic”按鈕(如下圖所示),進入VBA編輯頁面。

 

 

 最後引用xlwings庫即可。

  現在可以新建一個VBA模塊,然後編寫下面的代碼:  

 Sub test()
     RunPython ("import  hello; myproject.hello_xlwings()")
 End Sub   

運行腳本,就會看到在“A1”的位置插入了Hello xlwings!

  OK,現在大家已經了解了如何使用Python與Excel交互,其他還有很多種方法,而且也不僅僅只有Python能與Excel交互,其實幾乎所有的編程語言,甚至是C語言,都有想用的Library可以與Excel交互。那就期待我下面的文章吧!    

 

 

   

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

OAuth2.0系列之基本概念和運作流程(一)_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

@

目錄

  • 一、OAuth2.0是什麼?
    • 1.1 OAuth2.0簡介
    • 1.2 OAuth2.0官方文檔
  • 二、OAuth2.0原理
    • 2.1 OAuth2.0流程圖
  • 三、 OAuth2.0的角色
  • 四、OAuth2.0授權模式
    • 4.1 授權碼模式(authorization code)
    • 4.2 簡化模式(implicit grant type)
    • 4.3 密碼模式(resource owner password credentials)
    • 4.4 客戶端模式(client credentials)
  • 五、優質參考資料

一、OAuth2.0是什麼?

1.1 OAuth2.0簡介

OAuth 2.0是目前最流行的授權機制,用來授權第三方應用

OAuth是一種開放協議, 允許用戶讓第三方應用以安全且標準的方式獲取該用戶在某一網站,移動或者桌面應用上存儲的秘密的資源(如用戶個人信息,照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。

OAuth 1.0協議(RFC5849)作為一個指導性文檔發布,是一個小社區的工作成果。
本標準化規範在OAuth 1.0的部署經驗之上構建,也包括其他使用案例以及從更廣泛的IETF社區收集到的可擴展性需求。

OAuth 2.0協議不向後兼容OAuth 1.0。這兩個版本可以在網絡上共存,實現者可以選擇同時支持他們。

1.2 OAuth2.0官方文檔

官網:https://oauth.net/2/ ,官網只有英文版文檔,您也可以參考翻譯過來的文檔,鏈接:OAuth2 RFC6749中文翻譯

OAuth2.0在安全性方面做了比較大的提高,簡單來說OAuth2.0就是一種授權協議,可以用來授權,隨意點個網站,如圖這種網站,用戶不想註冊,就可以用微信、支付寶登錄,就是場景是很常見的,也是OAuth2.0的應用

二、OAuth2.0原理

OAuth2.0是一種授權機制,正常情況,不使用OAuth2.0等授權機制的系統,客戶端是可以直接訪問資源服務器的資源的,為了用戶安全訪問數據,在訪問中間添加了Access Token機制。客戶端需要攜帶Access Token去訪問受到保護的資源。所以OAuth2.0確保了資源不被惡意客戶端訪問,從而提高了系統的安全性。

2.1 OAuth2.0流程圖

引用官方圖片介紹OAuth2.0總體流程:

  • (A)客戶端向從資源所有者請求授權。
  • (B)客戶端收到授權許可,資源所有者給客戶端頒發授權許可(比如授權碼code)
  • (C)客戶端與授權服務器進行身份認證並出示授權許可(比如授權碼code)請求訪問令牌。
  • (D)授權服務器驗證客戶端身份並驗證授權許可,若有效則頒發訪問令牌(accept token)。
  • (E)客戶端從資源服務器請求受保護資源並出示訪問令牌(accept token)進行身份驗證。
  • (F)資源服務器驗證訪問令牌(accept token),若有效則滿足該請求。

三、 OAuth2.0的角色

OAuth2.0定義如下角色:

引用OAuth2 RFC6749中文翻譯:

  • 資源所有者(Resource Owner): 能夠許可受保護資源訪問權限的實體。當資源所有者是個人時,它作為最終用戶被提及。
  • 用戶代理(User Agent): 指的的資源擁有者授權的一些渠道。一般指的是瀏覽器、APP
  • 客戶端(Client) 使用資源所有者的授權代表資源所有者發起對受保護資源的請求的應用程序。術語“客戶端”並非特指任何特定的的實現特點(例如:應用程序是否在服務器、台式機或其他設備上執行)。
  • 授權服務器(Authorization Server): 在成功驗證資源所有者且獲得授權后頒發訪問令牌給客戶端的服務器。
    授權服務器和資源服務器之間的交互超出了本規範的範圍。授權服務器可以和資源服務器是同一台服務器,也可以是分離的個體。一個授權服務器可以頒發被多個資源服務器接受的訪問令牌。
  • 資源服務器(Resource Server): 託管受保護資源的服務器,能夠接收和響應使用訪問令牌對受保護資源的請求。

四、OAuth2.0授權模式

OAuth2.0有4種授權模式:

  • 授權碼模式(authorization code)
  • 簡化模式(implicit)
  • 密碼模式(resource owner password credentials)
  • 客戶端模式(client credentials)

其中最常用的是授權碼模式,4種授權模式的詳細介紹可以參考阮一峰老師的:OAuth 2.0 的四種方式

4.1 授權碼模式(authorization code)

授權碼(authorization code)方式,指的是第三方應用先申請一個授權碼,然後再用該碼獲取令牌。

官網圖片:

從調接口方面,簡單來說:

  • 第一步:獲取code:
    eg:oauthServer+”/oauth/authorize?client_id=”+clientId+”&response_type=code&redirect_uri=”+redirectUrl+”&scope=all”
    如果沒有登錄,則會跳轉到統一身份認證登錄頁面。如果用戶登錄了,調用接口后,會重定向到redirect_uri,授權碼會作為它的參數

  • 第二步:獲取access_token
    eg:oauthServer+”/oauth/token?code=”+code+”&grant_type=authorization_code&client_secret=”+clientSecret+”&redirect_uri=”+redirectUri+”&client_id=”+clientId

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

    台中搬家公司推薦超過30年經驗,首選台中大展搬家

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1MzQ5NzMsInVzZXJfbmFtZSI6Im5pY2t5IiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9hZG1pbiJdLCJqdGkiOiJmMjM0M2Q0NC1hODViLTQyOGYtOWE1ZS1iNTE4NTAwNTM5ODgiLCJjbGllbnRfaWQiOiJvYSIsInNjb3BlIjpbImFsbCJdfQ.LWkN2gC2dBrGTn5uSPzfdW6yRj7jhlX87EE8scY02hI",
    "token_type": "bearer",
    "expires_in": 59,
    "scope": "all",
    "user_name": "nicky",
    "jti": "f2343d44-a85b-428f-9a5e-b51850053988"
}
  • 第三步:訪問系統資源,此時統一認證服務會根據該認證客戶端權限信息判斷,決定是否返回信息。

訪問:http://localhost:8084/api/userinfo?access_token=${accept_token}

4.2 簡化模式(implicit grant type)

簡化模式(implicit grant type)不通過第三方應用程序的服務器,直接在瀏覽器中向認證服務器申請令牌,跳過了”授權碼”這個步驟,因此稱簡化模式

從調接口方面,簡單來說:

  • 第一步:訪問授權,要傳client_id:客戶端id,redirect_uri:重定向uri,response_type為token,scope是授權範圍,state是其它自定義參數

http://localhost:8888/oauth/authorize?client_id=cms&redirect_uri=http://localhost:8084/callback&response_type=token&scope=read&state=123

  • 第二步:授權通過,會重定向到redirect_uri,access_token碼會作為它的參數

http://localhost:8084/callback#access_token=${accept_token}&token_type=bearer&state=123&expires_in=120

  • 第三步:拿到acceptToken之後,就可以直接訪問資源

http://localhost:8084/api/userinfo?access_token=${accept_token}

4.3 密碼模式(resource owner password credentials)

密碼模式中,用戶向客戶端提供自己的用戶名和密碼,這通常用在用戶對客戶端高度信任的情況

從調接口方面,簡單來說:

  • 第一步:直接傳username,password獲取token

http://localhost:8888/oauth/token?client_id=cms&client_secret=secret&username=admin&password=123456&grant_type=password&scope=all

  • 第二步:拿到acceptToken之後,就可以直接訪問資源

http://localhost:8084/api/userinfo?access_token=${accept_token}

4.4 客戶端模式(client credentials)

客戶端模式(client credentials)適用於沒有前端的命令行應用,即在命令行下請求令牌

從調接口方面,簡單來說:

  • 第一步: 獲取token
    http://localhost:8888/oauth/token?client_id=cms&client_secret=123&grant_type=client_credentials&scope=all

  • 第二步:拿到acceptToken之後,就可以直接訪問資源

http://localhost:8084/api/userinfo?access_token=${accept_token}

學習必要的理論知識后,還是實踐一下才能明白整個流程,詳情參考我OAuth2.0系列博客專欄:OAuth2.0系列博客,SpringBoot的參考我系列博客專欄:SpringBoot系列博客

五、優質參考資料

  • OAuth 2.0 的四種方式
  • OAuth2.0系列博客專欄
  • OAuth2.0 JWT 認證授權
  • RFC6749官方參考文檔

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

Skype for Business Online 將於 7 月底結束,微軟開始提示商務用戶盡早轉移_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

Skype for Business Online直到現在還是很多企業、行號愛用的通訊工具,在 2019 年時,微軟已經提早預告將會在 2021 年 7 月底時終止這項服務,距離結束還有 6 個月,現在微軟也開始提醒商務用戶儘早升級、轉移到 Microsoft Teams,以免到時候手忙腳亂影響工作。

Skype for Business Online將於 7 月底結束,微軟開始提示商務用戶盡早轉移

雖然疫情的關係讓全球許多公司、企業原本的規劃大亂、行程也因而有所延宕,但這回 Skype for Business Online 的結束之日並不會因此而延後。微軟在一篇官方部落格中寫到,無論目前轉移進度如何,現在就是一個重要的檢視時間,以確保公司在 Skype for Business Online 停用前能夠升級到 Microsoft Teams,以利整體業務的無縫接軌。

在官方部落格裡面,微軟還提供了各搬遷階段的資源連結來幫助用戶的轉移:
Microsoft Teams 管理:這份文件將指導用戶如何管理、準備來使用 Microsoft Teams
Teams 升級規劃 workshops:引導用戶完成一個經驗正的框架,主要用於規劃和實際進行轉移工作
微軟粉筆講座 workshops:圍繞 Teams 中一些最受歡迎的功能提供最佳實作指導

Skype for Business Online 的用戶將擁有自動升級到 Microsoft Teams 的資格,用以幫助用戶直接升級,而計劃進行自動升級的用戶將在預計的升級日期前至少 3 個月前就會在 Teams 管理中心河 Microsoft 365 訊息中心裡面收到通知,以便有更充裕的時間進行技術層面與使用的事前準備。想瞭解更多有關自動升級的資訊,可閱讀官方說明文件。

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

OPPO Find X3 Pro 實機上手玩照片曝光:火山口設計主相機,並加入顯微鏡拍攝_網頁設計公司

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

傳聞將於 3 月登場的 OPPO Find X3 系列中備受矚目的 Find X3 Pro ,在前段時間來自 Evan Blass(evleaks)首曝過官方渲染圖,讓大家對於這款新機有了初步的概念。而今日稍早, evleaks 更是直接洩漏多張 OPPO Find X3 Pro 的實機外觀照片,從這批照片可以發現其實和之前的渲染圖幾乎吻合,不過實機的樣貌看起來還是相當特殊。

▲圖片來源:Evan Blass(Voice/@evleaks)

OPPO Find X3 Pro 實機上手玩照片曝光:火山口設計主相機,並加入顯微鏡拍攝

除了日前電腦王阿達為大家開箱的 OPPO Reno5/Reno5 Pro 開箱,相信也有不少人等待 2021 年 OPPO 在全新 Find X 系列會帶來哪些驚喜。稍早,爆料大神 Evan Blass(evleaks)釋出多張 Find X3 Pro 的實機外觀照片,與他之前分享的渲染圖的外觀造型可說是幾乎相同。這款將搭載 Qualcomm Snapdragon 888 的 Find X3 Pro 在機身背面和相機模組間有著流線的曲面弧度,而相機模組周圍微微隆起的「火山口」設計,據傳 Find X3 Pro 將推出玻璃和陶瓷兩種材質版本,相信對於生產也將帶來相當大的挑戰。

▲圖片來源:Evan Blass(Voice/@evleaks)

▲圖片來源:Evan Blass(Voice/@evleaks)

▲圖片來源:Evan Blass(Voice/@evleaks)

相機規格方面, Find X3 Pro 傳聞將搭載 5000 萬像素四鏡頭主相機的配置,其中包括 5000 萬像素的主鏡頭和超廣角鏡頭(SONY IMX766 感光元件),另外最特殊的是 Find X3 Pro 將加入一枚配有環形補光燈的 300 萬像素微距鏡頭,其 25 倍變焦可實現「顯微鏡」模式拍攝。至於第四顆鏡頭,則是去年就在 Find X2 搭載的 1300 萬像素 2x 長焦鏡頭( F2.4光圈、5P 鏡頭、5 倍混合變焦、最高 20倍數位變焦)。

▲圖片來源:Evan Blass(Voice/@evleaks)

在一月底,數碼閒聊站也在微博刊出一張手機的相機操作介面截圖,當時就曾出現過「顯微鏡」模式:

▲圖片來源:數碼閒聊站(微博)

最後關於 Find X3 系列發表的時間,傳聞 OPPO 有望於三月正式發表全新 Find X3 系列,包括 Find X3 Pro 、 Find X3 Neo 以及 Find X3 Lite ,其中 Find X3 Lite 日前的開箱照也證實就是台灣已推出的 Reno5 的更名版本。

消息來源:Evan Blass(Voice/@evleaks)|數碼閒聊站(微博)

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

延伸閱讀:
OPPO Find X3 Pro 更多關鍵規格曝光:將搭載高通 S888 處理器、12GB RAM、256GB ROM

ROG Phone 5 更多外觀、規格細節曝光:另有 Anime Matrix 顯示版本?

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

POCO X3 Pro 通過 FCC 認證,近期有望正式亮相_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

上個月 POCO 才剛以獨立品牌重返台灣市場推出性價比超高的 POCO M3 ,近期也傳聞 POCO 將再有新機發表。日前有款 POCO 的新機通過 FCC 等多國機構認證通過,而它就是 POCO X 系列中的 POCO X3 Pro ,預期將帶來比起 POCO X3/X3 NFC 更高階的旗艦規格,未來也不排除再正式發表後有機會引進台灣販售。

▲示意圖,圖為 POCO X3 NFC(圖片來源:POCO)

POCO X3 Pro 通過 FCC 認證,近期有望正式亮相

近期有款小米 POCO 品牌的新機陸續通過包括 FCC 、IMDA 、 EEC 以及 TUV 等認證,其型號為 M2102J20SG ,經比對是目前尚未發表的 POCO X3 Pro 。
雖然目前未有關於 POCO X3 Pro 的詳細規格資訊,不過回顧之前海外發表的 POCO X3 標準版的規格, POCO X3 標準版搭載高通 Snapdragon 732G 處理器、8GB RAM、 128GB ROM , POCO X3 配備 6.67 吋 FHD+ 解析度挖孔全螢幕,螢幕支持最高 120Hz 螢幕更新率和 240Hz 觸控採樣率,內建 6000mAh 大容量電池並支持 33W 快速充電,相機則搭載 6400 萬像素四鏡頭主相機,搭配側邊結合電源鍵的指紋辨識鍵,而 POCO X3 Pro 預期將帶來比上述更高一階的規格配置。

▲圖片來源:FCC

參考目前在印度 POCO 的產品線規劃, POCO X 系列定調為旗艦體驗的中高階機型,目前已在海外發表的POXO X3/X3 NFC 搭載高通 Snapdragon 732G 處理器,而近期通過認證的 POCO X3 Pro 預計將搭載旗艦級處理器。
▲圖片來源:POCO

雖然目前未確認 POCO X3 Pro 的詳細規格,不過有消息指出 POCO X3 Pro 其實就是印度市場 POCO F2 的國際版本,不過目前就連同 POCO F2 都尚未正式推出,因此無論是 POCO X3 Pro 或 POCO F2 可能都還得等上一段時間才會有進一步消息。

圖片/消息來源:FCC

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

延伸閱讀:
小米11 國際版將於2/8 晚間發表,有望 3 月在台亮相

OPPO Find X3 Pro 實機上手玩照片曝光:火山口設計主相機,並加入顯微鏡拍攝

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

面試三輪我倒在了一道sql題上——sql性能優化

一、前言

最近小農在找工作,因為今年疫情的特殊原因,導致工作不是特別好找,所以一旦有面試電話,如果可以,都會去試一試,剛好接到一個面試邀請,感覺公司還不錯,於是就確定了面試時間,準備了一下就去面試了。

第一輪面試是小組組長面試,通過。
第二輪是經理面試也是通過了。
第三輪總監面試,前面都還有模有樣,突然畫風一轉,面試官說:“問你最後一個問題”

面試官:10W條數據,我要從其中查出100條不連續的數據,給你id,來查name和password進行展示,如何才能高性能的去使用?

我:在id上建立聚簇索引,然後用 in id 來縮小表搜索範圍,最後 使用條件查詢 小於最大id,大於最小id,這樣可以讓sql速度能夠比較快的展示,雖然In的性能比較低
心裏活動:雕蟲小技,還最後一個問題,這樣的問題再來一個吧

只見面試官緊鎖眉頭,與我心裏期待的表情有點不一樣啊,難道是哪個環節出了問題?
面試官:這樣的性能不能達到最優化的程度,而且如果我給你的最小id是1,最大id是100000呢?

你這就有點杠精了啊,那行吧,你是面試官你說了算
我:既然id已經給出來了,而且只查詢兩個字段,用聚簇索引那麼查詢數據是很快的,用in id應該是可以的。

面試官:好的,回去等通知吧
我。。。。。

二、後知

於是回去后,查詢資料,才知道原來面試官,真正想考的是 “覆蓋索引”

什麼是覆蓋索引:

當sql語句的所求查詢字段(select列)和查詢條件字段(where子句)全都包含在一個索引中 (聯合索引),可以直接使用索引查詢而不需要回表。這就是覆蓋索引,通過使用覆蓋索引,可以減少搜索樹的次數,這就是 覆蓋索引,在了解覆蓋索引之前,我們先來看看什麼是索引。

三、什麼是索引?

我們有一個主鍵列為id的表,表中有字段name,並且在name上有索引

表中 t_user 值分別為(1,張一)、(2,張二)、(3,張三)、(4,張四)、(5,張五)

表結構如下:

mysql> create table t_user (
id bigint(20) not null auto_increment ,
name varchar(255) not null,
primary key (id),
index index_name (name) using btree)
engine=innodb
default character set=utf8 collate=utf8_general_ci

兩棵樹的示例示意圖如下:

從圖中不難看出,根據恭弘=叶 恭弘子節點的內容,索引類型分為主鍵索引和二級索引(非主鍵索引)。

主鍵索引: 主鍵索引的恭弘=叶 恭弘子節點保存着主鍵即對應行的全部數據。在InnoDB里,主鍵索引也被稱為聚簇索引(clustered index)。

二級索引(非主鍵索引): 二級索引樹中的恭弘=叶 恭弘子結點保存着索引值和主鍵值,當使用二級索引進行查詢時,需要進行回表操作。在InnoDB里,非主鍵索引也被稱為二級索引(secondary index)

通過上面所講的,我們來看看如何通過sql語句來區分 主鍵索引和普通索引的查詢

  • select * from t_user where id=1 即主鍵查詢方式,則只需要搜索id這棵B+樹
  • select * from t_user where name=張三 即普通索引查詢方式,則需要先搜索name索引樹,得到id的值為3,再到id索引樹搜索一次。這個過程稱為回表

也就是說,基於二級索引(非主鍵索引)的查詢需要多掃描一棵索引樹。因此,我們在應用中應該盡量使用主鍵查詢。

看到這裏如果你看懂了上面的介紹,那麼這裏你會有一個疑問,我直接用in id不就好了嗎,建立id主鍵索引,就可以不用回表了,速度不也就提升了嗎?

如果是 5.5 之前的版本確實不會走索引的,在 5.5 之後的版本,MySQL 做了優化。MySQL 在 2010 年發布 5.5 版本中,優化器對 in 操作符可以自動完成優化,針對建立了索引的列可以使用索引,沒有索引的列還是會走全表掃描,也就是我們所說的回表。

那麼,有沒有可能經過索引優化,避免回表過程呢?答應是有的

四、覆蓋索引

sql語句如下,其中id自增,name為索引:

mysql> create table t_user (
id bigint(20) not null auto_increment ,
name varchar(255) not null,
password varchar(255) ,
primary key (id),
engine=innodb
default character set=utf8 collate=utf8_general_ci

比如有這麼兩句sql

語句A: select id from user_table where name= '張三'
語句B: select password from user_table where name= '張三'

語句A: 因為 name索引樹 的恭弘=叶 恭弘子結點上保存有 name和id的值 ,所以通過 name索引樹 查找到id后,因此可以直接提供查詢結果,不需要回表,也就是說,在這個查詢裏面,索引name 已經 “覆蓋了” 我們的查詢需求,我們稱為 覆蓋索引

語句B: name索引樹 上 找到 name=’張三’ 對應的主鍵id, 通過回表在主鍵索引樹上找到滿足條件的數據

因此我們可以得知,當sql語句的所求查詢字段(select列)和查詢條件字段(where子句)全都包含在一個索引中(聯合索引),可以直接使用索引查詢而不需要回表。這就是覆蓋索引

例如上面的語句B是一個高頻查詢的語句,我們可以建立(name,password)的聯合索引,這樣,查詢的時候就不需要再去回表操作了,可以提高查詢效率。

所以關於上面的面試題我們就可以得出,使用聯合索引就可以很好的回答面試官的問題(id,name,password)這樣的聯合索引就可以調用到覆蓋索引,可以減少樹的搜索次數,不再需要回表查整行記錄,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。

說到了聯合索引我們就不得不說聯合索引中最重要的匹配原則,最左匹配原則了

五、最左匹配原則

最左前綴匹配原則,是非常重要的原則,mysql會從左向右進行匹配。

例如我們定義了(name,password)兩個聯合索引字段,我們 使用 where name = '張三' and password = '2' 索引可以生效的,當我們是顛倒了他們的順序 使用where password = '1' and name = '王五',索引同樣也是可以生效的,在mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃,我們能盡量的利用到索引時的查詢順序效率最高,所以mysql查詢優化器會最終以這種順序(where name = '張三' and password = '2' )進行查詢執行,就類似 我們的 order by name,password這樣一種排序規則,先對張三的用戶進行查詢排序,在對password進行處理

比如我們要查詢姓張的用戶,我們的條件查詢可以為 "where name like ‘張%’",但是不能是 where name like '%張%'或者是 where name like '%張',因為索引可以用於查詢條件字段為索引字段,根據字段值必須是最左若干個字符進行的模糊查詢,也就是需要是 ‘張%’ 這樣的添加才可以使用。

索引的復用能力。因為可以支持最左前綴,所以當已經有了(name,password)這個聯合索引后,一般就不需要單獨在name上建立索引了。因此,第一原則是,如果通過調整順序,可以少維護一個索引,那麼這個順序往往就是需要優先考慮採用的。

如果既有聯合查詢,又有基於name,password各自的查詢呢?查詢條件裏面只有password的語句,是無法使用(name,password)這個聯合索引的,這時候你需要同時維護(name,password)、(password) 這兩個索引。

創建索引時,我們也要考慮空間代價,使用較少的空間來創建索引
假設我們現在不需要通過name查詢password了,需要通過name查詢age或通過age查詢name

  • 1.(name,age)聯合索引+age單字段索引
  • 2.(age,name)聯合索引+name單字段索引

name字段是比age字段大的,所以,選擇第一種,索引佔用空間較小的一個

六、索引下推

上面我們說到滿足最左前綴原則的時候,最左前綴可以用於在索引中定位記錄。那麼如果那些不符合最左前綴的部分,會怎麼樣呢?

如果現在有一個需求:檢索出表中“名字第一個字是張,而且沒有刪除的信息(is_del = 1)。SQL語句如下:

mysql> select * from t_user where name like ‘張%’ and is_del=1

在MySQL 5.6之前,只能從匹配的位置一個個回表。到主鍵索引上找出數據行,再對比字段值

在MySQL 5.6中 引入的索引下推優化(index condition pushdown), 可以在索引遍歷過程中,對索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數

根據(username,is_del)聯合索引查詢所有滿足名稱以“張”開頭的索引,然後回表查詢出相應的全行數據,然後再篩選出未刪除的用戶數據。過程如下圖:

每一個虛線箭頭表示回表一次
圖一(無索引下推執行流程)

每一個虛線箭頭表示回表一次
圖二(索引下推執行流程)

圖1跟圖2的區別是,InnoDB在(name,is_del)索引內部就判斷了數據是否邏輯刪除,對於邏輯刪除的記錄,直接判斷並跳過。在我們的這個例子中,只需要對ID1、ID4這兩條記錄回表取數據判斷,就只需要回表2次

mysql默認啟用索引下推,我們也可以通過修改系統變量optimizer_switch的index_condition_pushdown標誌來控制SET optimizer_switch = 'index_condition_pushdown=off';

我們也需要注意:

  • innodb引擎的表,索引下推只能用於二級索引,因為innodb的主鍵索引樹恭弘=叶 恭弘子結點上保存的是全行數據,所以這個時候索引下推並不會起到減少查詢全行數據的效果
  • 索引下推一般可用於所求查詢字段(select列)不是/不全是聯合索引的字段,查詢條件為多條件查詢且查詢條件子句(where/order by)字段全是聯合索引

六、小結

今天的內容就到這裏了,我們在上面描述了數據庫索引的概念,包括了覆蓋索引、聯合索引、索引下推,那麼下次如果有面試官問你剛開始的問題,相信大家可以好好的回(dui)答(ta)一下面試官了,在sql優化中,減少回表次數,或者直接使用覆蓋索引是比較重要的,盡量少地訪問資源也是數據庫設計的重要原則之一,謝謝大家,加油~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

13萬就買大氣體面的中型車!車主們都是這車特值?

6T 自動旗艦型TOp裸車價格: 14。85萬滿意的地方:性價比比較高。裸車不到15萬,但是車子的配置非常齊全,胎壓監測、無鑰匙進入、全景天窗、感應後備廂等很多配置應有盡有。尤其是全景天窗,晚上打開天窗看星星,確實很浪漫。

現代名圖憑藉的就是錯位競爭,依靠自身的性價比優勢,在今年前九個月累計銷售了102138輛,表現很不錯,那麼我們今天就來看看車主如何評價他們的愛車的。

北京現代-名圖

指導價:12.98-17.68萬

長寬高:4710*1820*1470mm

軸距:2770mm

動力系統:

1.8L 143馬力+6擋手動/6擋手自一體

2.0L 155馬力+6擋手自一體

1.6T 175馬力+7擋雙離合

車主一

購買車型: 2016款 1.8L 自動智能型GLS

裸車價格: 12.60萬

滿意的地方:最滿意的還是外觀、空間和全景天窗。車子足夠大,足夠長,看起來很大氣,同時白色的也比較耐臟。後排空間很大,腿部空間很寬裕,因為空間足夠,所以做起來很舒服,後備箱也是挺大的,長途旅行很方便。車子優惠幅度很大,價格很滿意。目前車子的百公里油耗在9L左右。

沒有定速巡航,沒有ESp,新車味太大了,懸架偏硬,過減速帶會感到震動比較大,另外6AT變速箱也有點頓挫,沒有CVT那麼順滑。

車主二

購買車型: 2016款 1.6T 自動旗艦型TOp

裸車價格: 14.85萬

滿意的地方:性價比比較高。裸車不到15萬,但是車子的配置非常齊全,胎壓監測、無鑰匙進入、全景天窗、感應後備廂等很多配置應有盡有。尤其是全景天窗,晚上打開天窗看星星,確實很浪漫。1.6T發動機提速很猛烈,推背感還是非常明顯的。內飾和外觀一樣,看着很上檔次,空間也很大。目前車子的百公里油耗在8.5L左右。

起步的時候渦輪沒有介入,動力很差,但是之後就會明顯改善。另外全景天窗有時候會異響,座椅也不是真皮的,車漆有點薄。雙離合變速箱還是有點頓挫。

編輯總結:名圖主打的就是錯位競爭,再加上優惠力度較大,所以說價格方面還是很實惠的,但是也會存在配置不足,雙離合變速箱不是太好用等缺點。小編覺得如果是家用,1.8L的車型就足夠了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

當初哥就是因為錯信了這些廣告,花多了錢還買錯了車…

終身免保養,後期“0”費用買車容易養車難,這話我們都知道,所以不少經銷商就是抓住我們的這一顧慮,就推出終身免費保養政策。這種政策看似誘惑力十足,買了車後期費用都不用花了多好。其實不然只要你仔細翻看合約你就會發現,終身免保養免的只是每次小保養的費用,這種費用大多在300-500元之間,同時你還必須要在購車店才能享受,一但你在外面的修理店修了車你的車就等同於脫保了,那麼這保養政策也就失效了。

在這個互聯網信息時代,無論你是看電視、刷微博、上網、甚至是街道行走,只要你仔細觀察你就會發現你的周圍無時無刻充斥着各種廣告。這些廣告很多都是“失實”的,它們會有意無意得進入你的潛意識,對我們的生活造成滋擾。在我們需要選擇商品時往往會被這些軟文廣告忽悠得暈頭轉向,真是稍不留神就被騙得不要不要的。

作為“汽車媒體的搬運工”對於有關汽車的廣告更是深惡痛疾。一些虛假誇大產品的廣告詞似乎只要忽悠你買車,什麼底線、節操皆可拋,次品當精品宣傳,彎的說成直的。比起這些華而不實的廣告,我相信網友們更愛看真相,所以準備了一些反洗腦技巧給大家科普一下。

中國消費者都喜歡,這個廣告真是說到了咱們的心坎里去了,大即是美我們中國消費者都喜歡長軸距車。我身邊不少朋友都被這種廣告忽悠過,明明數據寫着是同級最長啊,怎麼一到車內就變窄了呢?其實非常好解釋,舉個例子拿昂克賽拉和本田凌派做對比,昂克賽拉2700mm的軸距比起凌派可要長了近50mm,可是在凌派上後排腿部空間要比昂克賽拉大上不少。究其原因主要是因為車輛的空間布局的方式不同,所以也就認證一個結論“空間與軸距沒有必然的關係”。

每次看到這廣告詞,就讓想起前些年駕駛的某些自主品牌的車,雖說宣稱是前後獨立懸挂,但實際的效果比起很多採用非獨立懸挂的車還要差。也不知的從何時開始,只要說到後輪獨立懸挂就硬生生被掰彎非要與運動性扯上關係。其實車輛底盤是否運動,更多時候是看底盤工程師對底盤的調校,懸挂的形式在其中只是起了很小的作用。

又到了年底,不少經銷商為了招攬客戶都會打出這樣的旗號,一些十來萬的車甚至給出了超過30%的優惠。但現實是否真的這麼划算呢?其實不然,經銷商也是要賺錢的,沒理由做虧本生意的。所以在新車上的優惠都會在保險或是其他地方掙回來,要牢記“羊毛出在羊身上”。一些特惠車型往往是4s店自身加裝了很多高價配置,比方說一個導航一萬、真皮座椅七千然後再降價30%銷售,這類的手法已經路見不鮮了,說好的優惠到了結賬時候反而更貴了,真是買的永遠沒賣的精。

買車容易養車難,這話我們都知道,所以不少經銷商就是抓住我們的這一顧慮,就推出終身免費保養政策。這種政策看似誘惑力十足,買了車後期費用都不用花了多好。其實不然只要你仔細翻看合約你就會發現,終身免保養免的只是每次小保養的費用,這種費用大多在300-500元之間,同時你還必須要在購車店才能享受,一但你在外面的修理店修了車你的車就等同於脫保了,那麼這保養政策也就失效了。換句話說這個優惠政策更多的是為了用免費保養把你捆綁在4s店內,每次保養就有機會向你推銷商品了,並且你車壞了也必須在4s店修,要知道4s店的修車費用可是要比外面高几倍不止。

對於這種廣告,建議大家還是笑笑看過就算了。越級上市,你當看電視劇呢?還越級。一些連同級車都還比不上的車就敢宣稱越級,真是可笑之極。另外說“豪華”二字,目前還沒有哪個機構能定義豪華的級別,你說豪華也就是你自認為的而已,現在的車只要是有個天窗加个中控大屏就憑空說自己是豪華車,想想也是醉了。

目前在售的城市SUV車型,為了豐富產品線多半都會搭載四驅系統,其實這都是無可厚非的,畢竟四驅系統這事對於北方雪季行駛有很大的幫助,但有些廠家不知道是否缺乏常識還是故意誇大產品。硬生生把適時四驅系統宣傳成全時四驅。要知道全時四驅系統多半是搭載載在硬派SUV上面的。而城市SUV搭載的都是適時四驅或者分時四驅,這幾種的四驅形式在越野能力上有着天壤之別。只有像牧馬人,帕傑羅這種車才是全時四驅,具體的四驅系統感興趣的同學可以點擊以前文章“都買SUV!他們真的懂什麼是SUV?”

Sorry不大懂,誰能告訴我中高級車是什麼鬼,我們只知道緊湊型車、中型車、中大型車。而這些標榜自己為中高級車的實際只是緊湊型車,是以家用為主的買菜車定位,廠商為了混淆我們的視線往往都會誇張得說自己是中高級車,給到我們買一台卡羅拉就要買了一台凱美瑞一樣的感覺,強行將自己往更高一個級別拽。

舉了這麼多例子,大家對汽車廣告都有一定的抵抗力了吧。要知道廣告有風險,剁手需謹慎啊,各位網友在買車前一定要看清楚廣告,仔仔細細的甄別虛虛實實,最好問下身邊人的意見,千萬別買到手了後悔喔。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!