h2database初始化流程

载入数据库驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Class.forName("org.h2.Driver");


//org.h2.Driver
private static final Driver INSTANCE = new Driver();

public static synchronized Driver load() {
try {
if (!registered) {
registered = true;
DriverManager.registerDriver(INSTANCE);
}
} catch (SQLException e) {
DbException.traceThrowable(e);
}
return INSTANCE;
}

获取 session(数据库会话/连接)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Connection conn = DriverManager.getConnection("jdbc:h2:~/test");

//org.h2.Driver#connect
public Connection connect(String url, Properties info) throws SQLException {
try {
if (info == null) {
info = new Properties();
}
if (!acceptsURL(url)) {
return null;
}
if (url.equals(DEFAULT_URL)) {
return DEFAULT_CONNECTION.get();
}
Connection c = DbUpgrade.connectOrUpgrade(url, info);
if (c != null) {
return c;
}
return new JdbcConnection(url, info);
} catch (Exception e) {
throw DbException.toSQLException(e);
}
}

创建session(数据库连接):初始化SessionFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
打开一个新的(远程或嵌入式)会话。
//org.h2.engine.SessionRemote#connectEmbeddedOrServer
public SessionInterface connectEmbeddedOrServer(boolean openNew) {
ConnectionInfo ci = connectionInfo;
if (ci.isRemote()) {
connectServer(ci);
return this;
}
// create the session using reflection,
// so that the JDBC layer can be compiled without it
boolean autoServerMode = ci.getProperty("AUTO_SERVER", false);
ConnectionInfo backup = null;
try {
if (autoServerMode) {
backup = ci.clone();
connectionInfo = ci.clone();
}
if (openNew) {
ci.setProperty("OPEN_NEW", "true");
}
if (sessionFactory == null) {
sessionFactory = (SessionFactory) Class.forName(
"org.h2.engine.Engine").getMethod("getInstance").invoke(null);
}
return sessionFactory.createSession(ci);
} catch (Exception re) {
DbException e = DbException.convert(re);
if (e.getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1) {
if (autoServerMode) {
String serverKey = ((JdbcException) e.getSQLException()).getSQL();
if (serverKey != null) {
backup.setServerKey(serverKey);
// OPEN_NEW must be removed now, otherwise
// opening a session with AUTO_SERVER fails
// if another connection is already open
backup.removeProperty("OPEN_NEW", null);
connectServer(backup);
return this;
}
}
}
throw e;
}
}

首先要设置 sessionFactory,利用Class.forName() 装载 org.h2.engine.Engine类并且对其实例化,生成了一个单例的 org.h2.engine.Engine对象赋值给sessionFactory

创建session(数据库会话/连接)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//org.h2.engine.Engine#openSession(org.h2.engine.ConnectionInfo)
private synchronized Session openSession(ConnectionInfo ci) {
boolean ifExists = ci.removeProperty("IFEXISTS", false);
boolean forbidCreation = ci.removeProperty("FORBID_CREATION", false);
boolean ignoreUnknownSetting = ci.removeProperty(
"IGNORE_UNKNOWN_SETTINGS", false);
String cipher = ci.removeProperty("CIPHER", null);
String init = ci.removeProperty("INIT", null);
Session session;
long start = System.nanoTime();
for (;;) {
session = openSession(ci, ifExists, forbidCreation, cipher);
if (session != null) {
break;
}
// we found a database that is currently closing
// wait a bit to avoid a busy loop (the method is synchronized)
if (System.nanoTime() - start > 60_000_000_000L) {
// retry at most 1 minute
throw DbException.get(ErrorCode.DATABASE_ALREADY_OPEN_1,
"Waited for database closing longer than 1 minute");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw DbException.get(ErrorCode.DATABASE_CALLED_AT_SHUTDOWN);
}
}
synchronized (session) {
session.setAllowLiterals(true);
DbSettings defaultSettings = DbSettings.getDefaultSettings();
for (String setting : ci.getKeys()) {
if (defaultSettings.containsKey(setting)) {
// database setting are only used when opening the database
continue;
}
String value = ci.getProperty(setting);
if (!ParserUtil.isSimpleIdentifier(setting, false, false)) {
throw DbException.get(ErrorCode.UNSUPPORTED_SETTING_1, setting);
}
try {
CommandInterface command = session.prepareCommand(
"SET " + setting + ' ' + value,
Integer.MAX_VALUE);
command.executeUpdate(null);
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.ADMIN_RIGHTS_REQUIRED) {
session.getTrace().error(e, "admin rights required; user: \"" +
ci.getUserName() + "\"");
} else {
session.getTrace().error(e, "");
}
if (!ignoreUnknownSetting) {
session.close();
throw e;
}
}
}
if (init != null) {
try {
CommandInterface command = session.prepareCommand(init,
Integer.MAX_VALUE);
command.executeUpdate(null);
} catch (DbException e) {
if (!ignoreUnknownSetting) {
session.close();
throw e;
}
}
}
session.setAllowLiterals(false);
session.commit(true);
}
return session;
}


private Session openSession(ConnectionInfo ci, boolean ifExists, boolean forbidCreation, String cipher) {
String name = ci.getName();
Database database;
ci.removeProperty("NO_UPGRADE", false);
boolean openNew = ci.getProperty("OPEN_NEW", false);
boolean opened = false;
User user = null;
synchronized (DATABASES) {
if (openNew || ci.isUnnamedInMemory()) {
database = null;
} else {
database = DATABASES.get(name);
}
if (database == null) {
String p = ci.getProperty("MV_STORE");
boolean exists = p == null ? Database.exists(name)
: Database.exists(name, Utils.parseBoolean(p, true, false));
if (!exists) {
if (ifExists) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_WITH_IF_EXISTS_1, name);
}
if (forbidCreation) {
throw DbException.get(ErrorCode.REMOTE_DATABASE_NOT_FOUND_1, name);
}
}
//使用ConnectionInfo创建Database
database = new Database(ci, cipher);
opened = true;
if (database.getAllUsers().isEmpty()) {
// users is the last thing we add, so if no user is around,
// the database is new (or not initialized correctly)
user = new User(database, database.allocateObjectId(),
ci.getUserName(), false);
user.setAdmin(true);
user.setUserPasswordHash(ci.getUserPasswordHash());
database.setMasterUser(user);
}
if (!ci.isUnnamedInMemory()) {
DATABASES.put(name, database);
}
}
}
if (opened) {
// start the thread when already synchronizing on the database
// otherwise a deadlock can occur when the writer thread
// opens a new database (as in recovery testing)
database.opened();
}
if (database.isClosing()) {
return null;
}
if (user == null) {
if (database.validateFilePasswordHash(cipher, ci.getFilePasswordHash())) {
if (ci.getProperty("AUTHREALM")== null) {
user = database.findUser(ci.getUserName());
if (user != null) {
if (!user.validateUserPasswordHash(ci.getUserPasswordHash())) {
user = null;
}
}
} else {
Authenticator authenticator = database.getAuthenticator();
if (authenticator==null) {
throw DbException.get(ErrorCode.AUTHENTICATOR_NOT_AVAILABLE, name);
} else {
try {
AuthenticationInfo authenticationInfo=new AuthenticationInfo(ci);
user = database.getAuthenticator().authenticate(authenticationInfo, database);
} catch (AuthenticationException authenticationError) {
database.getTrace(Trace.DATABASE).error(authenticationError,
"an error occurred during authentication; user: \"" +
ci.getUserName() + "\"");
}
}
}
}
if (opened && (user == null || !user.isAdmin())) {
// reset - because the user is not an admin, and has no
// right to listen to exceptions
database.setEventListener(null);
}
}
if (user == null) {
DbException er = DbException.get(ErrorCode.WRONG_USER_OR_PASSWORD);
database.getTrace(Trace.DATABASE).error(er, "wrong user or password; user: \"" +
ci.getUserName() + "\"");
database.removeSession(null);
throw er;
}
//Prevent to set _PASSWORD
ci.cleanAuthenticationInfo();
checkClustering(ci, database);

//创建Session
Session session = database.createSession(user, ci.getNetworkConnectionInfo());
if (session == null) {
// concurrently closing
return null;
}
if (ci.getProperty("JMX", false)) {
try {
Utils.callStaticMethod(
"org.h2.jmx.DatabaseInfo.registerMBean", ci, database);
} catch (Exception e) {
database.removeSession(session);
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, e, "JMX");
}
jmx = true;
}
return session;
}

这里很关键的一步是使用ConnectionInfo创建出一个Database,利用这个Database对象创建Session。创建Database过程中会创建系统表

创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//使用ConnectionInfo创建Database
database = new Database(ci, cipher);

public Database(ConnectionInfo ci, String cipher) {
if (ASSERT) {
META_LOCK_DEBUGGING.set(null);
META_LOCK_DEBUGGING_DB.set(null);
META_LOCK_DEBUGGING_STACK.set(null);
}
String name = ci.getName();
this.dbSettings = ci.getDbSettings();
this.compareMode = CompareMode.getInstance(null, 0);
this.persistent = ci.isPersistent();
this.filePasswordHash = ci.getFilePasswordHash();
this.fileEncryptionKey = ci.getFileEncryptionKey();
this.databaseName = name;
this.databaseShortName = parseDatabaseShortName();
this.maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB;
this.cipher = cipher;
this.accessModeData = StringUtils.toLowerEnglish(
ci.getProperty("ACCESS_MODE_DATA", "rw"));
this.autoServerMode = ci.getProperty("AUTO_SERVER", false);
this.autoServerPort = ci.getProperty("AUTO_SERVER_PORT", 0);
int defaultCacheSize = Utils.scaleForAvailableMemory(
Constants.CACHE_SIZE_DEFAULT);
this.cacheSize =
ci.getProperty("CACHE_SIZE", defaultCacheSize);
this.pageSize = ci.getProperty("PAGE_SIZE",
Constants.DEFAULT_PAGE_SIZE);
if ("r".equals(accessModeData)) {
readOnly = true;
}
String lockMethodName = ci.getProperty("FILE_LOCK", null);
if (dbSettings.mvStore && lockMethodName == null) {
fileLockMethod = autoServerMode ? FileLockMethod.FILE : FileLockMethod.FS;
} else {
fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
}
this.databaseURL = ci.getURL();
String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
if (listener != null) {
listener = StringUtils.trim(listener, true, true, "'");
setEventListenerClass(listener);
}
String modeName = ci.removeProperty("MODE", null);
if (modeName != null) {
mode = Mode.getInstance(modeName);
if (mode == null) {
throw DbException.get(ErrorCode.UNKNOWN_MODE_1, modeName);
}
}
this.logMode =
ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
this.javaObjectSerializerName =
ci.getProperty("JAVA_OBJECT_SERIALIZER", null);
this.allowBuiltinAliasOverride =
ci.getProperty("BUILTIN_ALIAS_OVERRIDE", false);
boolean closeAtVmShutdown =
dbSettings.dbCloseOnExit;
int traceLevelFile =
ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE,
TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut =
ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
this.cacheType = StringUtils.toUpperEnglish(
ci.removeProperty("CACHE_TYPE", Constants.CACHE_TYPE_DEFAULT));
this.ignoreCatalogs = ci.getProperty("IGNORE_CATALOGS",
dbSettings.ignoreCatalogs);
openDatabase(traceLevelFile, traceLevelSystemOut, closeAtVmShutdown, ci);
}

第一步先设置一些参数,第二步去创建数据库需要的对象。一些重要对象:

  • User: DBA

    1
    systemUser = new User(this, 0, SYSTEM_USER_NAME, true);
  • Schema: PUBLIC

    1
    2
    mainSchema = new Schema(this, Constants.MAIN_SCHEMA_ID, sysIdentifier(Constants.SCHEMA_MAIN), systemUser,
    true);
  • Schema:INFORMATION_SCHEMA

    1
    2
    infoSchema = new Schema(this, Constants.INFORMATION_SCHEMA_ID, sysIdentifier("INFORMATION_SCHEMA"), systemUser,
    true);
  • Session:systemSession lobSession

    1
    2
    systemSession = new Session(this, systemUser, ++nextSessionId);
    lobSession = new Session(this, systemUser, ++nextSessionId);
  • Table: SYS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //创建Table:SYS
    CreateTableData data = new CreateTableData();
    ArrayList<Column> cols = data.columns;
    Column columnId = new Column("ID", Value.INT);
    columnId.setNullable(false);
    cols.add(columnId);
    cols.add(new Column("HEAD", Value.INT));
    cols.add(new Column("TYPE", Value.INT));
    cols.add(new Column("SQL", Value.STRING));
    boolean create = true;
    if (pageStore != null) {
    create = pageStore.isNew();
    }
    data.tableName = "SYS";
    data.id = 0;
    data.temporary = false;
    data.persistData = persistent;
    data.persistIndexes = persistent;
    data.create = create;
    data.isHidden = true;
    data.session = systemSession;
    starting = true;
    meta = mainSchema.createTable(data);